From ee8952de10171831a0d6332ea49b7116627a3c3d Mon Sep 17 00:00:00 2001 From: Fernando Rodrigues Date: Sun, 14 Sep 2025 13:07:08 +0000 Subject: [PATCH 001/322] Revert "fix: change default integration_api to 3004" --- server/lib/readConfigFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index 918fa4c4..39f48be5 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -64,7 +64,7 @@ export const configSchema = z server: z.object({ integration_port: portSchema .optional() - .default(3004) + .default(3003) .transform(stoi) .pipe(portSchema.optional()), external_port: portSchema From 08c930e6cf3369232a9604eea363b3fce5f8eab9 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:32:18 +0000 Subject: [PATCH 002/322] update webauthen --- package-lock.json | 237 ++++++++++++++++++----------- package.json | 4 +- server/routers/auth/securityKey.ts | 37 +++-- 3 files changed, 170 insertions(+), 108 deletions(-) diff --git a/package-lock.json b/package-lock.json index d3b0f434..4d140f7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,8 +35,8 @@ "@react-email/components": "0.5.3", "@react-email/render": "^1.2.0", "@react-email/tailwind": "1.2.2", - "@simplewebauthn/browser": "^13.1.2", - "@simplewebauthn/server": "^9.0.3", + "@simplewebauthn/browser": "^13.2.0", + "@simplewebauthn/server": "^13.2.1", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", @@ -3663,34 +3663,101 @@ "tslib": "^2.8.1" } }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.4.0.tgz", - "integrity": "sha512-fJiYUBCJBDkjh347zZe5H81BdJ0+OGIg0X9z06v8xXUoql3MFeENUX0JsjCaVaU9A0L85PefLPGYkIoGpTnXLQ==", + "node_modules/@peculiar/asn1-cms": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.5.0.tgz", + "integrity": "sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.4.0", - "@peculiar/asn1-x509": "^2.4.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "@peculiar/asn1-x509-attr": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.5.0.tgz", + "integrity": "sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.5.0.tgz", + "integrity": "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.5.0.tgz", + "integrity": "sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-pkcs8": "^2.5.0", + "@peculiar/asn1-rsa": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.5.0.tgz", + "integrity": "sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.5.0.tgz", + "integrity": "sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-pfx": "^2.5.0", + "@peculiar/asn1-pkcs8": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "@peculiar/asn1-x509-attr": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-rsa": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.4.0.tgz", - "integrity": "sha512-6PP75voaEnOSlWR9sD25iCQyLgFZHXbmxvUfnnDcfL6Zh5h2iHW38+bve4LfH7a60x7fkhZZNmiYqAlAff9Img==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.5.0.tgz", + "integrity": "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.4.0", - "@peculiar/asn1-x509": "^2.4.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-schema": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.4.0.tgz", - "integrity": "sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.5.0.tgz", + "integrity": "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==", "license": "MIT", "dependencies": { "asn1js": "^3.0.6", @@ -3699,17 +3766,48 @@ } }, "node_modules/@peculiar/asn1-x509": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.4.0.tgz", - "integrity": "sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.5.0.tgz", + "integrity": "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.4.0", + "@peculiar/asn1-schema": "^2.5.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.5.0.tgz", + "integrity": "sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.0.tgz", + "integrity": "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-csr": "^2.5.0", + "@peculiar/asn1-ecc": "^2.5.0", + "@peculiar/asn1-pkcs9": "^2.5.0", + "@peculiar/asn1-rsa": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + } + }, "node_modules/@posthog/core": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.0.2.tgz", @@ -5102,15 +5200,15 @@ } }, "node_modules/@simplewebauthn/browser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.2.tgz", - "integrity": "sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.0.tgz", + "integrity": "sha512-N3fuA1AAnTo5gCStYoIoiasPccC+xPLx2YU88Dv0GeAmPQTWHETlZQq5xZ0DgUq1H9loXMWQH5qqUjcI7BHJ1A==", "license": "MIT" }, "node_modules/@simplewebauthn/server": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-9.0.3.tgz", - "integrity": "sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.1.tgz", + "integrity": "sha512-Inmfye5opZXe3HI0GaksqBnQiM7glcNySoG6DH1GgkO1Lh9dvuV4XSV9DK02DReUVX39HpcDob9nxHELjECoQw==", "license": "MIT", "dependencies": { "@hexagon/base64": "^1.1.27", @@ -5120,20 +5218,12 @@ "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8", - "@simplewebauthn/types": "^9.0.1", - "cross-fetch": "^4.0.0" + "@peculiar/x509": "^1.13.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.0.0" } }, - "node_modules/@simplewebauthn/types": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-9.0.1.tgz", - "integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT" - }, "node_modules/@smithy/abort-controller": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", @@ -8079,35 +8169,6 @@ "node": ">= 0.10" } }, - "node_modules/cross-fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/cross-fetch/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -16205,6 +16266,12 @@ "node": ">=0.8.8" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -17515,12 +17582,6 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -17685,6 +17746,24 @@ "fsevents": "~2.3.3" } }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -18036,22 +18115,6 @@ "node": ">= 8" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", diff --git a/package.json b/package.json index d03f7c2f..c0d71dab 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@react-email/components": "0.5.3", "@react-email/render": "^1.2.0", "@react-email/tailwind": "1.2.2", - "@simplewebauthn/browser": "^13.1.2", - "@simplewebauthn/server": "^9.0.3", + "@simplewebauthn/browser": "^13.2.0", + "@simplewebauthn/server": "^13.2.1", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index 6b014986..8c9b02a5 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -20,14 +20,16 @@ import type { GenerateAuthenticationOptionsOpts, VerifyAuthenticationResponseOpts, VerifiedRegistrationResponse, - VerifiedAuthenticationResponse -} from "@simplewebauthn/server"; -import type { + VerifiedAuthenticationResponse, AuthenticatorTransport, AuthenticatorTransportFuture, PublicKeyCredentialDescriptorJSON, PublicKeyCredentialDescriptorFuture -} from "@simplewebauthn/types"; +} from "@simplewebauthn/server"; +import { + isoUint8Array, + isoBase64URL +} from '@simplewebauthn/server/helpers'; import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; import { verifyPassword } from "@server/auth/password"; @@ -204,15 +206,15 @@ export async function startRegistration( .where(eq(securityKeys.userId, user.userId)); const excludeCredentials = existingSecurityKeys.map(key => ({ - id: new Uint8Array(Buffer.from(key.credentialId, 'base64')), - type: 'public-key' as const, + id: key.credentialId, + type: "public-key" as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined })); const options: GenerateRegistrationOptionsOpts = { rpName, rpID, - userID: user.userId, + userID: isoUint8Array.fromUTF8String( user.userId ), userName: user.email || user.username, attestationType: 'none', excludeCredentials, @@ -308,10 +310,10 @@ export async function verifyRegistration( // Store the security key in the database await db.insert(securityKeys).values({ - credentialId: Buffer.from(registrationInfo.credentialID).toString('base64'), + credentialId: registrationInfo.credential.id, userId: user.userId, - publicKey: Buffer.from(registrationInfo.credentialPublicKey).toString('base64'), - signCount: registrationInfo.counter || 0, + publicKey: Buffer.from(registrationInfo.credential.publicKey).toString('base64'), + signCount: registrationInfo.credential.counter || 0, transports: credential.response.transports ? JSON.stringify(credential.response.transports) : null, name: challengeData.securityKeyName, lastUsed: new Date().toISOString(), @@ -496,7 +498,7 @@ export async function startAuthentication( const { email } = parsedBody.data; try { - let allowCredentials: PublicKeyCredentialDescriptorFuture[] = []; + let allowCredentials; let userId; // If email is provided, get security keys for that specific user @@ -533,13 +535,10 @@ export async function startAuthentication( } allowCredentials = userSecurityKeys.map(key => ({ - id: new Uint8Array(Buffer.from(key.credentialId, 'base64')), + id: key.credentialId, type: 'public-key' as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined })); - } else { - // If no email provided, allow any security key (for resident key authentication) - allowCredentials = []; } const options: GenerateAuthenticationOptionsOpts = { @@ -653,9 +652,9 @@ export async function verifyAuthentication( expectedChallenge: challengeData.challenge, expectedOrigin: origin, expectedRPID: rpID, - authenticator: { - credentialID: Buffer.from(securityKey.credentialId, 'base64'), - credentialPublicKey: Buffer.from(securityKey.publicKey, 'base64'), + credential: { + id: securityKey.credentialId, + publicKey: Buffer.from(securityKey.publicKey, 'base64'), counter: securityKey.signCount, transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransportFuture[] : undefined }, @@ -714,4 +713,4 @@ export async function verifyAuthentication( ) ); } -} \ No newline at end of file +} \ No newline at end of file From 31896c9be9da3f8dbe78bfea5007a435e586f72f Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:12:46 +0000 Subject: [PATCH 003/322] cleanup --- server/routers/auth/securityKey.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index 8c9b02a5..ba357f51 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -207,7 +207,6 @@ export async function startRegistration( const excludeCredentials = existingSecurityKeys.map(key => ({ id: key.credentialId, - type: "public-key" as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined })); @@ -536,7 +535,6 @@ export async function startAuthentication( allowCredentials = userSecurityKeys.map(key => ({ id: key.credentialId, - type: 'public-key' as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined })); } From 76da2ee324ec999816822b9d9d73e9d2838eb35a Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:19:35 +0000 Subject: [PATCH 004/322] cleanup --- server/routers/auth/securityKey.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index ba357f51..62e4b997 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -16,19 +16,11 @@ import { } from "@simplewebauthn/server"; import type { GenerateRegistrationOptionsOpts, - VerifyRegistrationResponseOpts, GenerateAuthenticationOptionsOpts, - VerifyAuthenticationResponseOpts, - VerifiedRegistrationResponse, - VerifiedAuthenticationResponse, - AuthenticatorTransport, - AuthenticatorTransportFuture, - PublicKeyCredentialDescriptorJSON, - PublicKeyCredentialDescriptorFuture + AuthenticatorTransportFuture } from "@simplewebauthn/server"; import { - isoUint8Array, - isoBase64URL + isoUint8Array } from '@simplewebauthn/server/helpers'; import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; From 73cd82081a4f39c47c2efed9d0ccb43b2f5744a4 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 23 Sep 2025 16:51:08 +0000 Subject: [PATCH 005/322] fix securitykey --- server/routers/auth/securityKey.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index 62e4b997..a3389ba7 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -20,7 +20,7 @@ import type { AuthenticatorTransportFuture } from "@simplewebauthn/server"; import { - isoUint8Array + isoBase64URL } from '@simplewebauthn/server/helpers'; import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; @@ -205,7 +205,7 @@ export async function startRegistration( const options: GenerateRegistrationOptionsOpts = { rpName, rpID, - userID: isoUint8Array.fromUTF8String( user.userId ), + userID: isoBase64URL.toBuffer(user.userId), userName: user.email || user.username, attestationType: 'none', excludeCredentials, @@ -303,9 +303,9 @@ export async function verifyRegistration( await db.insert(securityKeys).values({ credentialId: registrationInfo.credential.id, userId: user.userId, - publicKey: Buffer.from(registrationInfo.credential.publicKey).toString('base64'), + publicKey: isoBase64URL.fromBuffer(registrationInfo.credential.publicKey), signCount: registrationInfo.credential.counter || 0, - transports: credential.response.transports ? JSON.stringify(credential.response.transports) : null, + transports: registrationInfo.credential.transports ? JSON.stringify(registrationInfo.credential.transports) : null, name: challengeData.securityKeyName, lastUsed: new Date().toISOString(), dateCreated: new Date().toISOString() @@ -644,7 +644,7 @@ export async function verifyAuthentication( expectedRPID: rpID, credential: { id: securityKey.credentialId, - publicKey: Buffer.from(securityKey.publicKey, 'base64'), + publicKey: isoBase64URL.toBuffer(securityKey.publicKey), counter: securityKey.signCount, transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransportFuture[] : undefined }, From 1352316492bc8fb6b33af5983b69b4044bafd520 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Tue, 23 Sep 2025 17:44:34 +0000 Subject: [PATCH 006/322] update securityKey --- server/routers/auth/securityKey.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index a3389ba7..7e131dfd 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -605,7 +605,7 @@ export async function verifyAuthentication( } // Find the security key in database - const credentialId = Buffer.from(credential.id, 'base64').toString('base64'); + const credentialId = credential.id; const [securityKey] = await db .select() .from(securityKeys) From 957cfdd5d7b62c4897966255f995e1d691995f34 Mon Sep 17 00:00:00 2001 From: Tim <82370418+Tim5965@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:42:48 +0200 Subject: [PATCH 007/322] Update nl-NL.json Minor changes (probably missed) --- messages/nl-NL.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index ba4ab637..250cccec 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -3,7 +3,7 @@ "setupNewOrg": "Nieuwe organisatie", "setupCreateOrg": "Nieuwe organisatie aanmaken", "setupCreateResources": "Bronnen aanmaken", - "setupOrgName": "Naam organisatie", + "setupOrgName": "Naam van de organisatie", "orgDisplayName": "Dit is de weergavenaam van uw organisatie.", "orgId": "Organisatie ID", "setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.", @@ -35,7 +35,7 @@ "createAccount": "Account Aanmaken", "viewSettings": "Instellingen weergeven", "delete": "Verwijderen", - "name": "naam", + "name": "Naam", "online": "Online", "offline": "Offline", "site": "Referentie", @@ -265,7 +265,7 @@ "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", "apiKeysList": "Uw API-sleutel", "apiKeysSave": "Uw API-sleutel opslaan", - "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.", "apiKeysInfo": "Uw API-sleutel is:", "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", "generate": "Genereren", @@ -994,7 +994,7 @@ "actionGetUser": "Gebruiker ophalen", "actionGetOrgUser": "Krijg organisatie-gebruiker", "actionListOrgDomains": "Lijst organisatie domeinen", - "actionCreateSite": "Site maken", + "actionCreateSite": "Site aanmaken", "actionDeleteSite": "Site verwijderen", "actionGetSite": "Site ophalen", "actionListSites": "Sites weergeven", From df92e413849f519f573e9b1a344db394392d1413 Mon Sep 17 00:00:00 2001 From: Lokowitz Date: Thu, 25 Sep 2025 19:55:36 +0000 Subject: [PATCH 008/322] added migration for simplewebauthn --- server/setup/migrationsPg.ts | 2 ++ server/setup/migrationsSqlite.ts | 2 ++ server/setup/scriptsPg/1.10.4.ts | 39 ++++++++++++++++++++++++++++ server/setup/scriptsSqlite/1.10.4.ts | 34 ++++++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 server/setup/scriptsPg/1.10.4.ts create mode 100644 server/setup/scriptsSqlite/1.10.4.ts diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index 04779f30..5c748c89 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -11,6 +11,7 @@ import m3 from "./scriptsPg/1.8.0"; import m4 from "./scriptsPg/1.9.0"; import m5 from "./scriptsPg/1.10.0"; import m6 from "./scriptsPg/1.10.2"; +import m7 from "./scriptsPg/1.10.4"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -23,6 +24,7 @@ const migrations = [ { version: "1.9.0", run: m4 }, { version: "1.10.0", run: m5 }, { version: "1.10.2", run: m6 }, + { version: "1.10.4", run: m7 }, // Add new migrations here as they are created ] as { version: string; diff --git a/server/setup/migrationsSqlite.ts b/server/setup/migrationsSqlite.ts index 654c2716..d7c6793f 100644 --- a/server/setup/migrationsSqlite.ts +++ b/server/setup/migrationsSqlite.ts @@ -29,6 +29,7 @@ import m24 from "./scriptsSqlite/1.9.0"; import m25 from "./scriptsSqlite/1.10.0"; import m26 from "./scriptsSqlite/1.10.1"; import m27 from "./scriptsSqlite/1.10.2"; +import m28 from "./scriptsSqlite/1.10.4"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -57,6 +58,7 @@ const migrations = [ { version: "1.10.0", run: m25 }, { version: "1.10.1", run: m26 }, { version: "1.10.2", run: m27 }, + { version: "1.10.4", run: m28 }, // Add new migrations here as they are created ] as const; diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts new file mode 100644 index 00000000..311e6dc2 --- /dev/null +++ b/server/setup/scriptsPg/1.10.4.ts @@ -0,0 +1,39 @@ +import { db } from "@server/db/pg/driver"; +import { sql } from "drizzle-orm"; +import { isoBase64URL } from "@simplewebauthn/server/helpers"; + +const version = "1.10.4"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + try { + await db.execute(sql`BEGIN`); + + const webauthnCredentialsQuery = await db.execute(sql`SELECT credentialId, publicKey FROM 'webauthnCredentials'`); + + const webauthnCredentials = webauthnCredentialsQuery.rows as { credentialId: string; publicKey: string }[]; + + for (const webauthnCredential of webauthnCredentials) { + const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); + await db.execute(sql` + UPDATE "webauthnCredentials" SET "credentialId" = ${credentialId} + `); + + const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); + await db.execute(sql` + UPDATE "webauthnCredentials" SET "publicKey" = ${publicKey} + `); + } + + await db.execute(sql`COMMIT`); + console.log(`Updated credentialId and publicKey`); + } catch (e) { + await db.execute(sql`ROLLBACK`); + console.log("Unable to update credentialId and publicKey"); + console.log(e); + throw e; + } + + console.log(`${version} migration complete`); +} diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts new file mode 100644 index 00000000..5c7f0a0e --- /dev/null +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -0,0 +1,34 @@ +import { APP_PATH } from "@server/lib/consts"; +import Database from "better-sqlite3"; +import path from "path"; +import { isoBase64URL } from "@simplewebauthn/server/helpers"; + +const version = "1.10.4"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + const location = path.join(APP_PATH, "db", "db.sqlite"); + const db = new Database(location); + + db.transaction(() => { + + const webauthnCredentials = db.prepare(`SELECT credentialId, publicKey FROM 'webauthnCredentials'`).all() as { + credentialId: string; publicKey: string + }[]; + + for (const webauthnCredential of webauthnCredentials) { + const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); + db.prepare( + `UPDATE 'webauthnCredentials' SET 'credentialId' = ?` + ).run(credentialId); + + const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); + db.prepare( + `UPDATE 'webauthnCredentials' SET 'publicKey' = ?` + ).run(publicKey); + } + })(); + + console.log(`${version} migration complete`); +} From e555d3c496c69da9687ee88d512ec087df4c4a55 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Thu, 25 Sep 2025 17:14:25 -0700 Subject: [PATCH 009/322] add server action proxies --- messages/en-US.json | 4 +- src/actions/server.ts | 394 ++++++++++++++++++++++++++ src/app/auth/reset-password/page.tsx | 20 +- src/app/favicon.ico | Bin 15406 -> 0 bytes src/app/page.tsx | 9 +- src/components/AutoLoginHandler.tsx | 36 ++- src/components/LoginForm.tsx | 131 +++++---- src/components/ResourceAuthPortal.tsx | 200 +++++++------ src/components/ValidateOidcToken.tsx | 33 ++- 9 files changed, 663 insertions(+), 164 deletions(-) create mode 100644 src/actions/server.ts delete mode 100644 src/app/favicon.ico diff --git a/messages/en-US.json b/messages/en-US.json index 2e97bcd1..94e0266c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomain sanitized", "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edit file: docker-compose.yml" + "resourceExposePortsEditFile": "Edit file: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } diff --git a/src/actions/server.ts b/src/actions/server.ts new file mode 100644 index 00000000..0aea77c7 --- /dev/null +++ b/src/actions/server.ts @@ -0,0 +1,394 @@ +"use server"; + +import { cookies } from "next/headers"; +import { ResponseT } from "@server/types/Response"; + +type CookieOptions = { + path?: string; + httpOnly?: boolean; + secure?: boolean; + sameSite?: "lax" | "strict" | "none"; + expires?: Date; + maxAge?: number; +}; + +function parseSetCookieString(setCookie: string): { + name: string; + value: string; + options: CookieOptions; +} { + const parts = setCookie.split(";").map((p) => p.trim()); + const [nameValue, ...attrParts] = parts; + const [name, ...valParts] = nameValue.split("="); + const value = valParts.join("="); // handles '=' inside JWT + + const options: CookieOptions = {}; + + for (const attr of attrParts) { + const [k, v] = attr.split("=").map((s) => s.trim()); + switch (k.toLowerCase()) { + case "path": + options.path = v; + break; + case "httponly": + options.httpOnly = true; + break; + case "secure": + options.secure = true; + break; + case "samesite": + options.sameSite = + v?.toLowerCase() as CookieOptions["sameSite"]; + break; + case "expires": + options.expires = new Date(v); + break; + case "max-age": + options.maxAge = parseInt(v, 10); + break; + } + } + + return { name, value, options }; +} + +async function makeApiRequest( + url: string, + method: "GET" | "POST", + body?: any, + additionalHeaders: Record = {} +): Promise> { + // Get existing cookies to forward + const allCookies = await cookies(); + const cookieHeader = allCookies.toString(); + + const headers: Record = { + "Content-Type": "application/json", + "X-CSRF-Token": "x-csrf-protection", + ...(cookieHeader && { Cookie: cookieHeader }), + ...additionalHeaders + }; + + let res: Response; + try { + res = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined + }); + } catch (fetchError) { + console.error("API request failed:", fetchError); + return { + data: null, + success: false, + error: true, + message: "Failed to connect to server. Please try again.", + status: 0 + }; + } + + // Handle Set-Cookie header + const rawSetCookie = res.headers.get("set-cookie"); + if (rawSetCookie) { + try { + const { name, value, options } = parseSetCookieString(rawSetCookie); + const allCookies = await cookies(); + allCookies.set(name, value, options); + } catch (cookieError) { + console.error("Failed to parse Set-Cookie header:", cookieError); + // Continue without setting cookies rather than failing + } + } + + let responseData; + try { + responseData = await res.json(); + } catch (jsonError) { + console.error("Failed to parse response JSON:", jsonError); + return { + data: null, + success: false, + error: true, + message: "Invalid response format from server. Please try again.", + status: res.status + }; + } + + if (!responseData) { + console.error("Invalid response structure:", responseData); + return { + data: null, + success: false, + error: true, + message: + "Invalid response structure from server. Please try again.", + status: res.status + }; + } + + // If the API returned an error, return the error message + if (!res.ok || responseData.error) { + return { + data: null, + success: false, + error: true, + message: + responseData.message || + `Server responded with ${res.status}: ${res.statusText}`, + status: res.status + }; + } + + // Handle successful responses where data can be null + if (responseData.success && responseData.data === null) { + return { + data: null, + success: true, + error: false, + message: responseData.message || "Success", + status: res.status + }; + } + + if (!responseData.data) { + console.error("Invalid response structure:", responseData); + return { + data: null, + success: false, + error: true, + message: + "Invalid response structure from server. Please try again.", + status: res.status + }; + } + + return { + data: responseData.data, + success: true, + error: false, + message: responseData.message || "Success", + status: res.status + }; +} + +// ============================================================================ +// AUTH TYPES AND FUNCTIONS +// ============================================================================ + +export type LoginRequest = { + email: string; + password: string; + code?: string; +}; + +export type LoginResponse = { + useSecurityKey?: boolean; + codeRequested?: boolean; + emailVerificationRequired?: boolean; + twoFactorSetupRequired?: boolean; +}; + +export type SecurityKeyStartRequest = { + email?: string; +}; + +export type SecurityKeyStartResponse = { + tempSessionId: string; + challenge: string; + allowCredentials: any[]; + timeout: number; + rpId: string; + userVerification: "required" | "preferred" | "discouraged"; +}; + +export type SecurityKeyVerifyRequest = { + credential: any; +}; + +export type SecurityKeyVerifyResponse = { + success: boolean; + message?: string; +}; + +export async function loginProxy( + request: LoginRequest +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/login`; + + console.log("Making login request to:", url); + + return await makeApiRequest(url, "POST", request); +} + +export async function securityKeyStartProxy( + request: SecurityKeyStartRequest +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/security-key/authenticate/start`; + + console.log("Making security key start request to:", url); + + return await makeApiRequest(url, "POST", request); +} + +export async function securityKeyVerifyProxy( + request: SecurityKeyVerifyRequest, + tempSessionId: string +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/security-key/authenticate/verify`; + + console.log("Making security key verify request to:", url); + + return await makeApiRequest( + url, + "POST", + request, + { + "X-Temp-Session-Id": tempSessionId + } + ); +} + +// ============================================================================ +// RESOURCE TYPES AND FUNCTIONS +// ============================================================================ + +export type ResourcePasswordRequest = { + password: string; +}; + +export type ResourcePasswordResponse = { + session?: string; +}; + +export type ResourcePincodeRequest = { + pincode: string; +}; + +export type ResourcePincodeResponse = { + session?: string; +}; + +export type ResourceWhitelistRequest = { + email: string; + otp?: string; +}; + +export type ResourceWhitelistResponse = { + otpSent?: boolean; + session?: string; +}; + +export type ResourceAccessResponse = { + success: boolean; + message?: string; +}; + +export async function resourcePasswordProxy( + resourceId: number, + request: ResourcePasswordRequest +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/resource/${resourceId}/password`; + + console.log("Making resource password request to:", url); + + return await makeApiRequest(url, "POST", request); +} + +export async function resourcePincodeProxy( + resourceId: number, + request: ResourcePincodeRequest +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/resource/${resourceId}/pincode`; + + console.log("Making resource pincode request to:", url); + + return await makeApiRequest(url, "POST", request); +} + +export async function resourceWhitelistProxy( + resourceId: number, + request: ResourceWhitelistRequest +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/resource/${resourceId}/whitelist`; + + console.log("Making resource whitelist request to:", url); + + return await makeApiRequest( + url, + "POST", + request + ); +} + +export async function resourceAccessProxy( + resourceId: number +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/resource/${resourceId}`; + + console.log("Making resource access request to:", url); + + return await makeApiRequest(url, "GET"); +} + +// ============================================================================ +// IDP TYPES AND FUNCTIONS +// ============================================================================ + +export type GenerateOidcUrlRequest = { + redirectUrl: string; +}; + +export type GenerateOidcUrlResponse = { + redirectUrl: string; +}; + +export type ValidateOidcUrlCallbackRequest = { + code: string; + state: string; + storedState: string; +}; + +export type ValidateOidcUrlCallbackResponse = { + redirectUrl: string; +}; + +export async function validateOidcUrlCallbackProxy( + idpId: string, + code: string, + expectedState: string, + stateCookie: string, + loginPageId?: number +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/idp/${idpId}/oidc/validate-callback${loginPageId ? "?loginPageId=" + loginPageId : ""}`; + + console.log("Making OIDC callback validation request to:", url); + + return await makeApiRequest(url, "POST", { + code: code, + state: expectedState, + storedState: stateCookie + }); +} + +export async function generateOidcUrlProxy( + idpId: number, + redirect: string, + orgId?: string +): Promise> { + const serverPort = process.env.SERVER_EXTERNAL_PORT; + const url = `http://localhost:${serverPort}/api/v1/auth/idp/${idpId}/oidc/generate-url${orgId ? `?orgId=${orgId}` : ""}`; + + console.log("Making OIDC URL generation request to:", url); + + return await makeApiRequest(url, "POST", { + redirectUrl: redirect || "/" + }); +} diff --git a/src/app/auth/reset-password/page.tsx b/src/app/auth/reset-password/page.tsx index 490f89f7..1245ca09 100644 --- a/src/app/auth/reset-password/page.tsx +++ b/src/app/auth/reset-password/page.tsx @@ -5,6 +5,8 @@ import ResetPasswordForm from "@app/components/ResetPasswordForm"; import Link from "next/link"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import { getTranslations } from "next-intl/server"; +import { internal } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; export const dynamic = "force-dynamic"; @@ -22,7 +24,19 @@ export default async function Page(props: { const t = await getTranslations(); if (user) { - redirect("/"); + let loggedOut = false; + try { + // log out the user if they are logged in + await internal.post( + "/auth/logout", + undefined, + await authCookieHeader() + ); + loggedOut = true; + } catch (e) {} + if (!loggedOut) { + redirect("/"); + } } let redirectUrl: string | undefined = undefined; @@ -45,8 +59,8 @@ export default async function Page(props: { diff --git a/src/app/favicon.ico b/src/app/favicon.ico deleted file mode 100644 index bcaab339d8dd0c5be5e558c11e1af040a73e72e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 zcmeI3e~=tS6~`y?TR({T}d);do(~PZ*U3M|JcNr&cZ5Rg{hSAmKzwf=PVJxL=zx~F)zt=Dx z?J$hJsY4T5_~qOZ{nEB$=L+7wQn|BlY|pgq9+$SgUQ>CWouSG*Pevr8ym{2$lv1TH zLg$Y>mm}9Gw3g0P>&+;g>;mUK8QUEiufHkME=p%zcA-a)Cw`f{_x0E|%DpXZl@1W> zX=|t}ZI>2NZ!Nr5@6os6-DaONL#SkI=a)6@m2=Yshl!`%`brt0M6bys9b7=bn_ouj4AiKXr+vn_&gTigX&!!rCUs}08%qaKnXuHb0a{rK; z?{-Q%$~w%xSKg_gt>OXT{b#!1b|bg>mHM^gC31fQoNK{%xwM_NXn0q~Rxp=ms**Xk zue=R7{$4{vIsZXtzMEFw85z6uVZ$g-GcE6PQWsh`Qh!rt!Aog8z3vAx%Gppu1K!kK zNgwZ%@f!c`Wb$r57*+{Q(N)1=+Rn$Rw)e`QmJF-U7z4hID949cF0luj8bFo#@-ApfxXLIf_31q(VEi0<68kijUpikr5M3!ug+{Awkv7=_+ z$Skq#nS%TNaozoK)i^>Iz0x*24g>Oe7jk+%aUYd?Ya<&1tqyF~(h5!G{!i?39RG}6 zJiJn`feg@*>zbKktjz^zvu=s4b{`b}$nEgl;_?mw7UuBj$@n{~x~%87585TYBm0E` z{jBBf2aaoy_xFSLGB&~YN$+{*n?ajq@5t?Xp^dCBfcLL>-o$tuEp&LlZc@JEVMnvb+z%`$tLmoA{Cez2@$|4Ki)_H2AE`Sne+Ze)N3}{kl1L zpX`nduV{|1Uz>Sw*{nt7U1zBEJM&zG$GMa7sryYiZZ1DIyBQ4Pk3erDa$h3txZjqt z-9zj0XgvScAirb8*9&mW>G{XDrd2vh){mepxFa*)m&OOwexL0gul+>ZTh}TE-40nW zZcp<3H`YZ@`W0Pk(yF*1%)_)tW{Ry7Y~Z>u{H^6@>>6#a78wM6mb=7AloR7j#64!- zIXI}JV;toUhQHT)Mn|cKEmiKL{+PsZmH0e`vse&qzd zdid`{5ySUMId(HGlk7>^XA$U8HT(enwn-?E1st`>T8d z!wpg1!Mpa+m3wC#jW(5S%GkriUH*@8{F<5fzN+I3e1>dcFxO!Xq>;{U_1IU~FRAv;_-tem$G_J`?2~y!+$sT&!b_ zW@Xx-zVJ@x-KzK{;2XVX3nMw~#l^G<;_9jPe;`$G4~_C^_)hF3F+B6JpX*94Svwx! z{s?1*-{36g`JPF~i#oXzN3sohwNN2bD9wd;yo{LV*w+^QlLlari`aB{SFI=L%?M?8 zFm8u8;}bXFR`t14jqTybxH>Le1+ROszn7$J=g5wpft{)IxV?Y$&r}7&cRvi4L0J_DZ6-3 z&MNNZ`zc9t5ABf)KHZVbkC&K}o4~Rpj;dfwDd#Ne+>cN9EO*IMzREQ)ey!#HBJH2T z*4-ufCUgFVSyA6Mj&{psJ>{)*46fV~ytVDm^t#9T^vcsDH!D6rV*=)u{DV=k-D&cx zItLx&hqa6y9}i52Aj=m5>~fcN#Oy22!0+kjot%1td~Aa?6*}U_t{^Yj-(?N8Wo|*Q zL)+JgT(ipg7~_v$>Q%;sx>H@3gZ8Dglon0HsKdPXjR&BZJx2KVr>@7w0mH5niF{6e*QV(ym z+_XKLx#>+2zwDfK2i;JtH@(RDF=9hPrwHjXJZmQ-Q>!x=}xNh7tZOARl`dQVsnK`^s;_)8Vy*TAvyoq^9FUkDNIxEJ(CZsvJ~F6?_a*C#afK0{3`l6WizC3a}1KO=cPf32)wctGSs{!jLF zUY5PYuq@b@`XuEwHh{8*I>a1XiC>}j&cigP`s`z>#NSS*8u}1(JK68#?(aP|hq3;65XXoPHQG;3 z2;Gp}A386-#9CsreWaa#e#Tp^bENh1T~q7NYB_6UBv^lgGfQ#((Yby9{E(Ix@yz*B zPjtvs?)_SqZ09%ht-d^4{3d$HICIX2^GwlxNIZ=%nX$YZC)=~c?>_GjV!PYebN4sQ#*kAt>+Oem-tGb;d~mN zjHO;*g#R1JQ1%(I9}<%~kmhPn~i_t&%F1HTyAY`L;w-49!A12~w|ve#b^c2&85PFP}_ ziM4Ob;jYyuc(ay09GpQ8uu41-JV);m6KC09rQdpg|IHl>Z$!sM5kG{vdIs};89H`3 zbLuoO9>;Zv=$+WRj{f3aoJl!~dU9qXh}&Z2vswFx3_C0@9hEUWiQ)Cy+g=~Kt-9uT zat1SsJ$M(JMnCS_tnzEUKPA&@e5Np$FHdLPyp;L=`dg?UZ?HyOJ3S`97RWw)*JsXH z_rWhdOkMwD3-u3uZY_r=el<4dJo^45Sl^6!QG)+9@NPZ-$QmYbXUcNFB70xxRpea5 z=33X7tG07y-7{=qg-QI(nH8Nw72T4&ygo0#3i-&suI%LwAg}fM^7oo3lmB8QU;7wq z=AHAebfI`eVpuI!l)Qr2;xzxmk$seuJ$$ICh7Z&K*Wo`xsY3CiwIsfFJ2A%5lH4iS+52(o?N`eBEq?8Docp?3 z`WY8L#?ju~Wu1M)d!q07`cBmE7R=J06`WVOf<3hp#4c=ZJc;S!Po?CXq;j7DzhIZS fyOL+wFF9B8H~KsWv6RGGiRr#w@ofog0SWvchs=Li diff --git a/src/app/page.tsx b/src/app/page.tsx index 5c150c58..06b6b61c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -29,10 +29,13 @@ export default async function Page(props: { const getUser = cache(verifySession); const user = await getUser({ skipCheckVerifyEmail: true }); - const setupRes = await internal.get< + let complete = false; + try { + const setupRes = await internal.get< AxiosResponse - >(`/auth/initial-setup-complete`, await authCookieHeader()); - const complete = setupRes.data.data.complete; + >(`/auth/initial-setup-complete`, await authCookieHeader()); + complete = setupRes.data.data.complete; + } catch (e) {} if (!complete) { redirect("/auth/initial-setup"); } diff --git a/src/components/AutoLoginHandler.tsx b/src/components/AutoLoginHandler.tsx index c489a759..f7183076 100644 --- a/src/components/AutoLoginHandler.tsx +++ b/src/components/AutoLoginHandler.tsx @@ -3,9 +3,8 @@ import { useEffect, useState } from "react"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { createApiClient, formatAxiosError } from "@app/lib/api"; -import { GenerateOidcUrlResponse } from "@server/routers/idp"; import { AxiosResponse } from "axios"; -import { useRouter } from "next/navigation"; +import { redirect, useRouter } from "next/navigation"; import { Card, CardHeader, @@ -16,6 +15,7 @@ import { import { Alert, AlertDescription } from "@app/components/ui/alert"; import { Loader2, CheckCircle2, AlertCircle } from "lucide-react"; import { useTranslations } from "next-intl"; +import { generateOidcUrlProxy } from "@app/actions/server"; type AutoLoginHandlerProps = { resourceId: number; @@ -40,24 +40,38 @@ export default function AutoLoginHandler({ async function initiateAutoLogin() { setLoading(true); + let doRedirect: string | undefined; try { - const res = await api.post< - AxiosResponse - >(`/auth/idp/${skipToIdpId}/oidc/generate-url`, { + const response = await generateOidcUrlProxy( + skipToIdpId, redirectUrl - }); + ); - if (res.data.data.redirectUrl) { - // Redirect to the IDP for authentication - window.location.href = res.data.data.redirectUrl; + if (response.error) { + setError(response.message); + setLoading(false); + return; + } + + const data = response.data; + const url = data?.redirectUrl; + if (url) { + doRedirect = url; } else { setError(t("autoLoginErrorNoRedirectUrl")); } - } catch (e) { + } catch (e: any) { console.error("Failed to generate OIDC URL:", e); - setError(formatAxiosError(e, t("autoLoginErrorGeneratingUrl"))); + setError( + t("autoLoginErrorGeneratingUrl", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); } finally { setLoading(false); + if (doRedirect) { + redirect(doRedirect); + } } } diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 65e57156..2bef4ac0 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -28,7 +28,6 @@ import { AxiosResponse } from "axios"; import { formatAxiosError } from "@app/lib/api"; import { LockIcon, FingerprintIcon } from "lucide-react"; import { createApiClient } from "@app/lib/api"; -import { useEnvContext } from "@app/hooks/useEnvContext"; import { InputOTP, InputOTPGroup, @@ -42,6 +41,14 @@ import { GenerateOidcUrlResponse } from "@server/routers/idp"; import { Separator } from "./ui/separator"; import { useTranslations } from "next-intl"; import { startAuthentication } from "@simplewebauthn/browser"; +import { + generateOidcUrlProxy, + loginProxy, + securityKeyStartProxy, + securityKeyVerifyProxy +} from "@app/actions/server"; +import { redirect as redirectTo } from "next/navigation"; +import { useEnvContext } from "@app/hooks/useEnvContext"; export type LoginFormIDP = { idpId: number; @@ -70,6 +77,9 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const [showSecurityKeyPrompt, setShowSecurityKeyPrompt] = useState(false); const t = useTranslations(); + const currentHost = typeof window !== "undefined" ? window.location.hostname : ""; + const expectedHost = new URL(env.app.dashboardUrl).host; + const isExpectedHost = currentHost === expectedHost; const formSchema = z.object({ email: z.string().email({ message: t("emailInvalid") }), @@ -102,39 +112,39 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { try { // Start WebAuthn authentication without email - const startRes = await api.post( - "/auth/security-key/authenticate/start", - {} - ); + const startResponse = await securityKeyStartProxy({}); - if (!startRes) { - setError( - t("securityKeyAuthError", { - defaultValue: - "Failed to start security key authentication" - }) - ); + if (startResponse.error) { + setError(startResponse.message); return; } - const { tempSessionId, ...options } = startRes.data.data; + const { tempSessionId, ...options } = startResponse.data!; // Perform WebAuthn authentication try { - const credential = await startAuthentication(options); + const credential = await startAuthentication({ + optionsJSON: { + ...options, + userVerification: options.userVerification as + | "required" + | "preferred" + | "discouraged" + } + }); // Verify authentication - const verifyRes = await api.post( - "/auth/security-key/authenticate/verify", + const verifyResponse = await securityKeyVerifyProxy( { credential }, - { - headers: { - "X-Temp-Session-Id": tempSessionId - } - } + tempSessionId ); - if (verifyRes) { + if (verifyResponse.error) { + setError(verifyResponse.message); + return; + } + + if (verifyResponse.success) { if (onLogin) { await onLogin(); } @@ -208,30 +218,44 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { setShowSecurityKeyPrompt(false); try { - const res = await api.post>( - "/auth/login", - { - email, - password, - code + const response = await loginProxy({ + email, + password, + code + }); + + if (response.error) { + setError(response.message); + return; + } + + const data = response.data; + + // Handle case where data is null (e.g., already logged in) + if (!data) { + if (onLogin) { + await onLogin(); } - ); + return; + } - const data = res.data.data; - - if (data?.useSecurityKey) { + if (data.useSecurityKey) { await initiateSecurityKeyAuth(); return; } - if (data?.codeRequested) { + if (data.codeRequested) { setMfaRequested(true); setLoading(false); mfaForm.reset(); return; } - if (data?.emailVerificationRequired) { + if (data.emailVerificationRequired) { + if (!isExpectedHost) { + setError(t("emailVerificationRequired", { dashboardUrl: env.app.dashboardUrl })); + return; + } if (redirect) { router.push(`/auth/verify-email?redirect=${redirect}`); } else { @@ -240,7 +264,11 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { return; } - if (data?.twoFactorSetupRequired) { + if (data.twoFactorSetupRequired) { + if (!isExpectedHost) { + setError(t("twoFactorSetupRequired", { dashboardUrl: env.app.dashboardUrl })); + return; + } const setupUrl = `/auth/2fa/setup?email=${encodeURIComponent(email)}${redirect ? `&redirect=${encodeURIComponent(redirect)}` : ""}`; router.push(setupUrl); return; @@ -275,25 +303,26 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { } async function loginWithIdp(idpId: number) { + let redirectUrl: string | undefined; try { - const res = await api.post>( - `/auth/idp/${idpId}/oidc/generate-url`, - { - redirectUrl: redirect || "/" - } + const data = await generateOidcUrlProxy( + idpId, + redirect || "/" ); - - console.log(res); - - if (!res) { - setError(t("loginError")); + const url = data.data?.redirectUrl; + if (data.error) { + setError(data.message); return; } - - const data = res.data.data; - window.location.href = data.redirectUrl; - } catch (e) { - console.error(formatAxiosError(e)); + if (url) { + redirectUrl = url; + } + } catch (e: any) { + setError(e.message || t("loginError")); + console.error(e); + } + if (redirectUrl) { + redirectTo(redirectUrl); } } @@ -355,7 +384,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
{t("passwordForgot")} diff --git a/src/components/ResourceAuthPortal.tsx b/src/components/ResourceAuthPortal.tsx index c9877857..3018d7f2 100644 --- a/src/components/ResourceAuthPortal.tsx +++ b/src/components/ResourceAuthPortal.tsx @@ -39,6 +39,12 @@ import { AuthWithWhitelistResponse } from "@server/routers/resource"; import ResourceAccessDenied from "@app/components/ResourceAccessDenied"; +import { + resourcePasswordProxy, + resourcePincodeProxy, + resourceWhitelistProxy, + resourceAccessProxy, +} from "@app/actions/server"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; @@ -173,100 +179,126 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { return fullUrl.toString(); } - const onWhitelistSubmit = (values: any) => { + const onWhitelistSubmit = async (values: any) => { setLoadingLogin(true); - api.post>( - `/auth/resource/${props.resource.id}/whitelist`, - { email: values.email, otp: values.otp } - ) - .then((res) => { - setWhitelistError(null); + setWhitelistError(null); - if (res.data.data.otpSent) { - setOtpState("otp_sent"); - submitOtpForm.setValue("email", values.email); - toast({ - title: t("otpEmailSent"), - description: t("otpEmailSentDescription") - }); - return; - } + try { + const response = await resourceWhitelistProxy(props.resource.id, { + email: values.email, + otp: values.otp + }); - const session = res.data.data.session; - if (session) { - window.location.href = appendRequestToken( - props.redirect, - session - ); - } - }) - .catch((e) => { - console.error(e); - setWhitelistError( - formatAxiosError(e, t("otpEmailErrorAuthenticate")) - ); - }) - .then(() => setLoadingLogin(false)); - }; - - const onPinSubmit = (values: z.infer) => { - setLoadingLogin(true); - api.post>( - `/auth/resource/${props.resource.id}/pincode`, - { pincode: values.pin } - ) - .then((res) => { - setPincodeError(null); - const session = res.data.data.session; - if (session) { - window.location.href = appendRequestToken( - props.redirect, - session - ); - } - }) - .catch((e) => { - console.error(e); - setPincodeError( - formatAxiosError(e, t("pincodeErrorAuthenticate")) - ); - }) - .then(() => setLoadingLogin(false)); - }; - - const onPasswordSubmit = (values: z.infer) => { - setLoadingLogin(true); - - api.post>( - `/auth/resource/${props.resource.id}/password`, - { - password: values.password + if (response.error) { + setWhitelistError(response.message); + return; } - ) - .then((res) => { - setPasswordError(null); - const session = res.data.data.session; - if (session) { - window.location.href = appendRequestToken( - props.redirect, - session - ); - } - }) - .catch((e) => { - console.error(e); - setPasswordError( - formatAxiosError(e, t("passwordErrorAuthenticate")) + + const data = response.data!; + if (data.otpSent) { + setOtpState("otp_sent"); + submitOtpForm.setValue("email", values.email); + toast({ + title: t("otpEmailSent"), + description: t("otpEmailSentDescription") + }); + return; + } + + const session = data.session; + if (session) { + window.location.href = appendRequestToken( + props.redirect, + session ); - }) - .finally(() => setLoadingLogin(false)); + } + } catch (e: any) { + console.error(e); + setWhitelistError( + t("otpEmailErrorAuthenticate", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); + } finally { + setLoadingLogin(false); + } + }; + + const onPinSubmit = async (values: z.infer) => { + setLoadingLogin(true); + setPincodeError(null); + + try { + const response = await resourcePincodeProxy(props.resource.id, { + pincode: values.pin + }); + + if (response.error) { + setPincodeError(response.message); + return; + } + + const session = response.data!.session; + if (session) { + window.location.href = appendRequestToken( + props.redirect, + session + ); + } + } catch (e: any) { + console.error(e); + setPincodeError( + t("pincodeErrorAuthenticate", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); + } finally { + setLoadingLogin(false); + } + }; + + const onPasswordSubmit = async (values: z.infer) => { + setLoadingLogin(true); + setPasswordError(null); + + try { + const response = await resourcePasswordProxy(props.resource.id, { + password: values.password + }); + + if (response.error) { + setPasswordError(response.message); + return; + } + + const session = response.data!.session; + if (session) { + window.location.href = appendRequestToken( + props.redirect, + session + ); + } + } catch (e: any) { + console.error(e); + setPasswordError( + t("passwordErrorAuthenticate", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); + } finally { + setLoadingLogin(false); + } }; async function handleSSOAuth() { let isAllowed = false; try { - await api.get(`/resource/${props.resource.id}`); - isAllowed = true; + const response = await resourceAccessProxy(props.resource.id); + if (response.error) { + setAccessDenied(true); + } else { + isAllowed = true; + } } catch (e) { setAccessDenied(true); } diff --git a/src/components/ValidateOidcToken.tsx b/src/components/ValidateOidcToken.tsx index 6b453ee5..7ba8e145 100644 --- a/src/components/ValidateOidcToken.tsx +++ b/src/components/ValidateOidcToken.tsx @@ -17,6 +17,7 @@ import { Alert, AlertDescription } from "@/components/ui/alert"; import { Loader2, CheckCircle2, AlertCircle } from "lucide-react"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; +import { validateOidcUrlCallbackProxy } from "@app/actions/server"; type ValidateOidcTokenParams = { orgId: string; @@ -54,17 +55,27 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { } try { - const res = await api.post< - AxiosResponse - >(`/auth/idp/${props.idpId}/oidc/validate-callback`, { - code: props.code, - state: props.expectedState, - storedState: props.stateCookie - }); + const response = await validateOidcUrlCallbackProxy( + props.idpId, + props.code || "", + props.expectedState || "", + props.stateCookie || "" + ); - console.log(t('idpOidcTokenResponse'), res.data); + if (response.error) { + setError(response.message); + setLoading(false); + return; + } - const redirectUrl = res.data.data.redirectUrl; + const data = response.data; + if (!data) { + setError("Unable to validate OIDC token"); + setLoading(false); + return; + } + + const redirectUrl = data.redirectUrl; if (!redirectUrl) { router.push("/"); @@ -74,9 +85,9 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { await new Promise((resolve) => setTimeout(resolve, 100)); if (redirectUrl.startsWith("http")) { - window.location.href = res.data.data.redirectUrl; // this is validated by the parent using this component + window.location.href = data.redirectUrl; // this is validated by the parent using this component } else { - router.push(res.data.data.redirectUrl); + router.push(data.redirectUrl); } } catch (e) { setError(formatAxiosError(e, t('idpErrorOidcTokenValidating'))); From a71b0a89247db161e94e2879042b3d39afce0e16 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:18 -0700 Subject: [PATCH 010/322] New translations en-us.json (Spanish) --- messages/es-ES.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/es-ES.json b/messages/es-ES.json index 0a835b33..17c44014 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdominio saneado", "domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml" + "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 9f66e09e4423112f771dcb1f62e617f893746d93 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:19 -0700 Subject: [PATCH 011/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index f617a768..a884474a 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomain sanitized", "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edit file: docker-compose.yml" + "resourceExposePortsEditFile": "Edit file: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 1ec3e53e11e5ce17a2f819da9a7c27181cc0a1f9 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:20 -0700 Subject: [PATCH 012/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 7b391431..22a64460 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomain sanitized", "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edit file: docker-compose.yml" + "resourceExposePortsEditFile": "Edit file: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 0472dc1b251ba19056eec2f33c17a3300862f172 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:22 -0700 Subject: [PATCH 013/322] New translations en-us.json (German) --- messages/de-DE.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index ed5d2042..57ec1bd3 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -513,7 +513,7 @@ "ipAddressErrorInvalidFormat": "Ungültiges IP-Adressformat", "ipAddressErrorInvalidOctet": "Ungültiges IP-Adress-Oktett", "path": "Pfad", - "matchPath": "Unterverzeichnis", + "matchPath": "Spielpfad", "ipAddressRange": "IP-Bereich", "rulesErrorFetch": "Fehler beim Abrufen der Regeln", "rulesErrorFetchDescription": "Beim Abrufen der Regeln ist ein Fehler aufgetreten", @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomain bereinigt", "domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml" + "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From b8b256da2e280f2e41ea8c2c77ec32dee787c134 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:23 -0700 Subject: [PATCH 014/322] New translations en-us.json (Italian) --- messages/it-IT.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index f0a862cd..57e9514f 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Sottodominio igienizzato", "domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Modifica file: docker-compose.yml" + "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 30790fdcb6b9e203a8f8a6a41077a6eaf4e8086a Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:24 -0700 Subject: [PATCH 015/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 64a449d0..f73190cf 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "하위 도메인 정리됨", "domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다", "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "파일 편집: docker-compose.yml" + "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 53bfaac0c075e02f52afe011ec3537f0d2d4f11e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:26 -0700 Subject: [PATCH 016/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 50 +++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index ba4ab637..b0a67894 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -38,12 +38,12 @@ "name": "naam", "online": "Online", "offline": "Offline", - "site": "Referentie", - "dataIn": "Dataverbruik inkomend", - "dataOut": "Dataverbruik uitgaand", + "site": "Website", + "dataIn": "Gegevens in", + "dataOut": "Data Uit", "connectionType": "Type verbinding", "tunnelType": "Tunnel type", - "local": "Lokaal", + "local": "lokaal", "edit": "Bewerken", "siteConfirmDelete": "Verwijderen van site bevestigen", "siteDelete": "Site verwijderen", @@ -55,7 +55,7 @@ "siteCreate": "Site maken", "siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden", "siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen", - "close": "Sluiten", + "close": "Afsluiten", "siteErrorCreate": "Fout bij maken site", "siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden", "siteErrorCreateDefaults": "Standaardinstellingen niet gevonden", @@ -90,7 +90,7 @@ "siteGeneralDescription": "Algemene instellingen voor deze site configureren", "siteSettingDescription": "Configureer de instellingen op uw site", "siteSetting": "{siteName} instellingen", - "siteNewtTunnel": "Newttunnel (Aanbevolen)", + "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", @@ -104,7 +104,7 @@ "siteCredentialsSave": "Uw referenties opslaan", "siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "siteInfo": "Site informatie", - "status": "Status", + "status": "status", "shareTitle": "Beheer deellinks", "shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen", "shareSearch": "Zoek share links...", @@ -146,19 +146,19 @@ "never": "Nooit", "shareErrorSelectResource": "Selecteer een bron", "resourceTitle": "Bronnen beheren", - "resourceDescription": "Veilige proxy's voor uw privé applicaties aanmaken", + "resourceDescription": "Veilige proxy's voor uw privé applicaties maken", "resourcesSearch": "Zoek bronnen...", "resourceAdd": "Bron toevoegen", "resourceErrorDelte": "Fout bij verwijderen document", "authentication": "Authenticatie", - "protected": "Beveiligd", - "notProtected": "Niet beveiligd", + "protected": "Beschermd", + "notProtected": "Niet beschermd", "resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.", "resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.", "resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?", "resourceHTTP": "HTTPS bron", "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", - "resourceRaw": "TCP/UDP bron", + "resourceRaw": "Ruwe TCP/UDP bron", "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", "resourceCreate": "Bron maken", "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", @@ -183,7 +183,7 @@ "protocolSelect": "Selecteer een protocol", "resourcePortNumber": "Nummer van poort", "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", - "cancel": "Annuleren", + "cancel": "annuleren", "resourceConfig": "Configuratie tekstbouwstenen", "resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen", "resourceAddEntrypoints": "Traefik: Entrypoints toevoegen", @@ -212,7 +212,7 @@ "saveGeneralSettings": "Algemene instellingen opslaan", "saveSettings": "Instellingen opslaan", "orgDangerZone": "Gevaarlijke zone", - "orgDangerZoneDescription": "Deze instantie verwijderen is onomkeerbaar. Bevestig alstublieft dat u wilt doorgaan.", + "orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.", "orgDelete": "Verwijder organisatie", "orgDeleteConfirm": "Bevestig Verwijderen Organisatie", "orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.", @@ -501,8 +501,8 @@ "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", "methodSelect": "Selecteer methode", "targetSubmit": "Doelwit toevoegen", - "targetNoOne": "Geen doel toegevoegd. Voeg deze toe via dit formulier.", - "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal load balancering mogelijk maken.", + "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", + "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", "targetsSubmit": "Doelstellingen opslaan", "proxyAdditional": "Extra Proxy-instellingen", "proxyAdditionalDescription": "Configureer hoe de proxy-instellingen van uw bron worden afgehandeld", @@ -598,7 +598,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", - "sites": "Verbindingen", + "sites": "Werkruimtes", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", @@ -729,7 +729,7 @@ "idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.", "idpConfirmDelete": "Bevestig verwijderen Identity Provider", "idpDelete": "Identity Provider verwijderen", - "idp": "Identiteitsaanbieders", + "idp": "Identiteit aanbieders", "idpSearch": "Identiteitsaanbieders zoeken...", "idpAdd": "Identity Provider toevoegen", "idpClientIdRequired": "Client-ID is vereist.", @@ -801,7 +801,7 @@ "defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.", "defaultMappingsSubmit": "Standaard toewijzingen opslaan", "orgPoliciesEdit": "Organisatie beleid bewerken", - "org": "Organisatie", + "org": "Rekening", "orgSelect": "Selecteer organisatie", "orgSearch": "Zoek in org", "orgNotFound": "Geen org gevonden.", @@ -976,10 +976,10 @@ "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", "githubUsername": "GitHub-gebruikersnaam", "supportKeyInput": "Supporter Sleutel", - "supportKeyBuy": "Koop supportersleutel", + "supportKeyBuy": "Koop Supportersleutel", "logoutError": "Fout bij uitloggen", "signingAs": "Ingelogd als", - "serverAdmin": "Server beheer", + "serverAdmin": "Server Beheerder", "managedSelfhosted": "Beheerde Self-Hosted", "otpEnable": "Twee-factor inschakelen", "otpDisable": "Tweestapsverificatie uitschakelen", @@ -1128,7 +1128,7 @@ "sidebarOverview": "Overzicht.", "sidebarHome": "Startpagina", "sidebarSites": "Werkruimtes", - "sidebarResources": "Bronnen", + "sidebarResources": "Hulpmiddelen", "sidebarAccessControl": "Toegangs controle", "sidebarUsers": "Gebruikers", "sidebarInvitations": "Uitnodigingen", @@ -1147,7 +1147,7 @@ "viewDockerContainers": "Bekijk Docker containers", "containersIn": "Containers in {siteName}", "selectContainerDescription": "Selecteer een container om als hostnaam voor dit doel te gebruiken. Klik op een poort om een poort te gebruiken.", - "containerName": "Naam", + "containerName": "naam", "containerImage": "Afbeelding", "containerState": "Provincie", "containerNetworks": "Netwerken", @@ -1349,7 +1349,7 @@ "olmId": "Olm ID", "olmSecretKey": "Olm Geheime Sleutel", "clientCredentialsSave": "Uw referenties opslaan", - "clientCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer deze naar een veilige plek.", + "clientCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "generalSettingsDescription": "Configureer de algemene instellingen voor deze client", "clientUpdated": "Klant bijgewerkt ", "clientUpdatedDescription": "De client is bijgewerkt.", @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomein gesaniseerd", "domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml" + "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 1c0dfa830eb69a7302df8003eb1e6f0297b935bd Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:27 -0700 Subject: [PATCH 017/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 4fe382e1..fb2d021d 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Poddomena oczyszczona", "domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml" + "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 97102b9be9f555e6b644261677bc934bee1accc5 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:28 -0700 Subject: [PATCH 018/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 9fd73d49..72db4444 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Subdomínio banalizado", "domainPickerSubdomainCorrected": "\"{sub}\" foi corrigido para \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml" + "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From fc76899384b574afcf0bd8604459423a7061a6df Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:30 -0700 Subject: [PATCH 019/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 9c38cc11..a7758452 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Субдомен очищен", "domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml" + "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 06477b6e7f4a0c01a22f913461c4937f1b56f4f9 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:31 -0700 Subject: [PATCH 020/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index ef812850..47821f97 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Alt alan adı temizlendi", "domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi", "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml" + "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 702b5eb3dd246177487985ad33ed3e9bee0d7719 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:32 -0700 Subject: [PATCH 021/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index c78d7460..fc85369e 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "子域已净化", "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"", "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "编辑文件:docker-compose.yml" + "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From 66d310fcca60282a9eee39886209c6bb59565966 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:34 -0700 Subject: [PATCH 022/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index ef5c0d2a..b9439f68 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Underdomenet som ble sanivert", "domainPickerSubdomainCorrected": "\"{sub}\" var korrigert til \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml" + "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From dc234beab1f69defc1502309f1e3fa2ce0e16b7a Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Thu, 25 Sep 2025 17:15:35 -0700 Subject: [PATCH 023/322] New translations en-us.json (French) --- messages/fr-FR.json | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 0918f943..8aa3f192 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -10,7 +10,7 @@ "setupErrorIdentifier": "L'ID de l'organisation est déjà pris. Veuillez en choisir un autre.", "componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.", "componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.", - "welcome": "Bienvenue sur Pangolin", + "welcome": "Bienvenue à Pangolin", "welcomeTo": "Bienvenue chez", "componentsCreateOrg": "Créer une organisation", "componentsMember": "Vous êtes membre de {count, plural, =0 {aucune organisation} one {une organisation} other {# organisations}}.", @@ -34,13 +34,13 @@ "confirmPassword": "Confirmer le mot de passe", "createAccount": "Créer un compte", "viewSettings": "Afficher les paramètres", - "delete": "Supprimer", + "delete": "Supprimez", "name": "Nom", "online": "En ligne", "offline": "Hors ligne", "site": "Site", - "dataIn": "Données reçues", - "dataOut": "Données envoyées", + "dataIn": "Données dans", + "dataOut": "Données épuisées", "connectionType": "Type de connexion", "tunnelType": "Type de tunnel", "local": "Locale", @@ -175,7 +175,7 @@ "resourceHTTPSSettingsDescription": "Configurer comment votre ressource sera accédée via HTTPS", "domainType": "Type de domaine", "subdomain": "Sous-domaine", - "baseDomain": "Domaine racine", + "baseDomain": "Domaine de base", "subdomnainDescription": "Le sous-domaine où votre ressource sera accessible.", "resourceRawSettings": "Paramètres TCP/UDP", "resourceRawSettingsDescription": "Configurer comment votre ressource sera accédée via TCP/UDP", @@ -309,7 +309,7 @@ "numberOfSites": "Nombre de sites", "licenseKeySearch": "Rechercher des clés de licence...", "licenseKeyAdd": "Ajouter une clé de licence", - "type": "Type", + "type": "Type de texte", "licenseKeyRequired": "La clé de licence est requise", "licenseTermsAgree": "Vous devez accepter les conditions de licence", "licenseErrorKeyLoad": "Impossible de charger les clés de licence", @@ -598,7 +598,7 @@ "newtId": "ID Newt", "newtSecretKey": "Clé secrète Newt", "architecture": "Architecture", - "sites": "Sites", + "sites": "Espaces", "siteWgAnyClients": "Utilisez n'importe quel client WireGuard pour vous connecter. Vous devrez adresser vos ressources internes en utilisant l'IP du pair.", "siteWgCompatibleAllClients": "Compatible avec tous les clients WireGuard", "siteWgManualConfigurationRequired": "Configuration manuelle requise", @@ -1128,7 +1128,7 @@ "sidebarOverview": "Aperçu", "sidebarHome": "Domicile", "sidebarSites": "Espaces", - "sidebarResources": "Ressources", + "sidebarResources": "Ressource", "sidebarAccessControl": "Contrôle d'accès", "sidebarUsers": "Utilisateurs", "sidebarInvitations": "Invitations", @@ -1519,5 +1519,7 @@ "domainPickerSubdomainSanitized": "Sous-domaine nettoyé", "domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml" + "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", + "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." } From fff4883bcaa7812047759d98ee06b4a4918b5c87 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Fri, 26 Sep 2025 18:25:19 +0530 Subject: [PATCH 024/322] Link to View Settings in API Keys --- src/components/OrgApiKeysTable.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/OrgApiKeysTable.tsx b/src/components/OrgApiKeysTable.tsx index bfe8dfab..52030b66 100644 --- a/src/components/OrgApiKeysTable.tsx +++ b/src/components/OrgApiKeysTable.tsx @@ -124,7 +124,9 @@ export default function OrgApiKeysTable({ setSelected(r); }} > - {t("viewSettings")} + + {t("viewSettings")} + { From 52aa27025d4ca536ced3a39d03b8aba06e319137 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:15:42 -0700 Subject: [PATCH 025/322] Bump the dev-patch-updates group across 1 directory with 2 updates (#1543) Bumps the dev-patch-updates group with 2 updates in the / directory: [tsx](https://github.com/privatenumber/tsx) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `tsx` from 4.20.5 to 4.20.6 - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.20.5...v4.20.6) Updates `typescript-eslint` from 8.44.0 to 8.44.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.44.1/packages/typescript-eslint) --- updated-dependencies: - dependency-name: tsx dependency-version: 4.20.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: typescript-eslint dependency-version: 8.44.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 132 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index b01ce7c8..5e060e0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,9 +125,9 @@ "react-email": "4.2.11", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", - "tsx": "4.20.5", + "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.44.0" + "typescript-eslint": "^8.44.1" } }, "node_modules/@alloc/quick-lru": { @@ -6489,16 +6489,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz", - "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", + "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.0", - "@typescript-eslint/type-utils": "8.44.0", - "@typescript-eslint/utils": "8.44.0", - "@typescript-eslint/visitor-keys": "8.44.0", + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/type-utils": "8.44.1", + "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -6512,7 +6512,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.0", + "@typescript-eslint/parser": "^8.44.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -6527,15 +6527,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz", - "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", + "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.0", - "@typescript-eslint/types": "8.44.0", - "@typescript-eslint/typescript-estree": "8.44.0", - "@typescript-eslint/visitor-keys": "8.44.0", + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", "debug": "^4.3.4" }, "engines": { @@ -6551,13 +6551,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz", - "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", + "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.0", - "@typescript-eslint/types": "^8.44.0", + "@typescript-eslint/tsconfig-utils": "^8.44.1", + "@typescript-eslint/types": "^8.44.1", "debug": "^4.3.4" }, "engines": { @@ -6572,13 +6572,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz", - "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", + "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.0", - "@typescript-eslint/visitor-keys": "8.44.0" + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6589,9 +6589,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz", - "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", + "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6605,14 +6605,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz", - "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", + "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.0", - "@typescript-eslint/typescript-estree": "8.44.0", - "@typescript-eslint/utils": "8.44.0", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/utils": "8.44.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6629,9 +6629,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", - "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", + "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6642,15 +6642,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz", - "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", + "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.0", - "@typescript-eslint/tsconfig-utils": "8.44.0", - "@typescript-eslint/types": "8.44.0", - "@typescript-eslint/visitor-keys": "8.44.0", + "@typescript-eslint/project-service": "8.44.1", + "@typescript-eslint/tsconfig-utils": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6722,15 +6722,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz", - "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", + "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.0", - "@typescript-eslint/types": "8.44.0", - "@typescript-eslint/typescript-estree": "8.44.0" + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6745,12 +6745,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz", - "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", + "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/types": "8.44.1", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -17665,9 +17665,9 @@ } }, "node_modules/tsx": { - "version": "4.20.5", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", - "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", "dependencies": { @@ -17819,16 +17819,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz", - "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", + "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.44.0", - "@typescript-eslint/parser": "8.44.0", - "@typescript-eslint/typescript-estree": "8.44.0", - "@typescript-eslint/utils": "8.44.0" + "@typescript-eslint/eslint-plugin": "8.44.1", + "@typescript-eslint/parser": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/utils": "8.44.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index ba806eeb..90766f85 100644 --- a/package.json +++ b/package.json @@ -142,9 +142,9 @@ "react-email": "4.2.11", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", - "tsx": "4.20.5", + "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.44.0" + "typescript-eslint": "^8.44.1" }, "overrides": { "emblor": { From 4710bab697cbb77425154ad9b6f7203162bc9bed Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 26 Sep 2025 09:57:35 -0700 Subject: [PATCH 026/322] pull hostname from dashboard url in crowdsec install --- install/main.go | 79 +++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/install/main.go b/install/main.go index 1f7213a1..0b791959 100644 --- a/install/main.go +++ b/install/main.go @@ -10,6 +10,7 @@ import ( "math/rand" "net" "net/http" + "net/url" "os" "os/exec" "path/filepath" @@ -21,9 +22,9 @@ import ( // DO NOT EDIT THIS FUNCTION; IT MATCHED BY REGEX IN CICD func loadVersions(config *Config) { - config.PangolinVersion = "1.9.4" - config.GerbilVersion = "1.2.1" - config.BadgerVersion = "1.2.0" + config.PangolinVersion = "replaceme" + config.GerbilVersion = "replaceme" + config.BadgerVersion = "replaceme" } //go:embed config/* @@ -48,9 +49,9 @@ type Config struct { TraefikBouncerKey string DoCrowdsecInstall bool Secret string - HybridMode bool - HybridId string - HybridSecret string + HybridMode bool + HybridId string + HybridSecret string } type SupportedContainer string @@ -190,7 +191,13 @@ func main() { return } - config.DashboardDomain = appConfig.DashboardURL + parsedURL, err := url.Parse(appConfig.DashboardURL) + if err != nil { + fmt.Printf("Error parsing URL: %v\n", err) + return + } + + config.DashboardDomain = parsedURL.Hostname() config.LetsEncryptEmail = traefikConfig.LetsEncryptEmail config.BadgerVersion = traefikConfig.BadgerVersion @@ -205,17 +212,17 @@ func main() { } } - config.InstallationContainerType = podmanOrDocker(reader) + config.InstallationContainerType = podmanOrDocker(reader) config.DoCrowdsecInstall = true - err := installCrowdsec(config) - if (err != nil) { - fmt.Printf("Error installing CrowdSec: %v\n", err) - return - } + err := installCrowdsec(config) + if err != nil { + fmt.Printf("Error installing CrowdSec: %v\n", err) + return + } - fmt.Println("CrowdSec installed successfully!") - return + fmt.Println("CrowdSec installed successfully!") + return } } } @@ -537,12 +544,12 @@ func printSetupToken(containerType SupportedContainer, dashboardDomain string) { tokenStart := strings.Index(trimmedLine, "Token:") if tokenStart != -1 { token := strings.TrimSpace(trimmedLine[tokenStart+6:]) - fmt.Printf("Setup token: %s\n", token) - fmt.Println("") - fmt.Println("This token is required to register the first admin account in the web UI at:") - fmt.Printf("https://%s/auth/initial-setup\n", dashboardDomain) - fmt.Println("") - fmt.Println("Save this token securely. It will be invalid after the first admin is created.") + fmt.Printf("Setup token: %s\n", token) + fmt.Println("") + fmt.Println("This token is required to register the first admin account in the web UI at:") + fmt.Printf("https://%s/auth/initial-setup\n", dashboardDomain) + fmt.Println("") + fmt.Println("Save this token securely. It will be invalid after the first admin is created.") return } } @@ -634,21 +641,21 @@ func run(name string, args ...string) error { } func checkPortsAvailable(port int) error { - addr := fmt.Sprintf(":%d", port) - ln, err := net.Listen("tcp", addr) - if err != nil { - return fmt.Errorf( - "ERROR: port %d is occupied or cannot be bound: %w\n\n", - port, err, - ) - } - if closeErr := ln.Close(); closeErr != nil { - fmt.Fprintf(os.Stderr, - "WARNING: failed to close test listener on port %d: %v\n", - port, closeErr, - ) - } - return nil + addr := fmt.Sprintf(":%d", port) + ln, err := net.Listen("tcp", addr) + if err != nil { + return fmt.Errorf( + "ERROR: port %d is occupied or cannot be bound: %w\n\n", + port, err, + ) + } + if closeErr := ln.Close(); closeErr != nil { + fmt.Fprintf(os.Stderr, + "WARNING: failed to close test listener on port %d: %v\n", + port, closeErr, + ) + } + return nil } func checkIsPangolinInstalledWithHybrid() bool { From a92f7dbb7ce46821452f21adc86a75d3f88b7904 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:50 -0700 Subject: [PATCH 027/322] New translations en-us.json (Spanish) --- messages/es-ES.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/es-ES.json b/messages/es-ES.json index 17c44014..55d046df 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.", + "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí." } From 1438eef62b433193f6f2e99d56f3f7fe255d69f2 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:51 -0700 Subject: [PATCH 028/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 2896 +++++++++++++++++++++---------------------- 1 file changed, 1448 insertions(+), 1448 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index a884474a..6814a6ba 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -18,17 +18,17 @@ "dismiss": "Отхвърляне", "componentsLicenseViolation": "Нарушение на лиценза: Сървърът използва {usedSites} сайта, което надвишава лицензионния лимит от {maxSites} сайта. Проверете лицензионните условия, за да се възползвате от всички функционалности.", "componentsSupporterMessage": "Благодарим ви, че подкрепяте Pangolin като {tier}!", - "inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.", - "inviteErrorUser": "We're sorry, but it looks like the invite you're trying to access is not for this user.", - "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.", - "goHome": "Go Home", - "inviteLogInOtherUser": "Log In as a Different User", - "createAnAccount": "Create an Account", - "inviteNotAccepted": "Invite Not Accepted", - "authCreateAccount": "Create an account to get started", - "authNoAccount": "Don't have an account?", + "inviteErrorNotValid": "Съжаляваме, но изглежда, че поканата, до която се опитвате да получите достъп, не е приета или вече не е валидна.", + "inviteErrorUser": "Съжаляваме, но изглежда, че поканата, до която се опитвате да получите достъп, не е предназначена за този потребител.", + "inviteLoginUser": "Моля, уверете се, че сте влезли като правилния потребител.", + "inviteErrorNoUser": "Съжаляваме, но изглежда, че поканата, до която се опитвате да получите достъп, не е за съществуващ потребител.", + "inviteCreateUser": "Моля, първо създайте акаунт.", + "goHome": "Отиди вкъщи", + "inviteLogInOtherUser": "Влезте като друг потребител", + "createAnAccount": "Създайте профил", + "inviteNotAccepted": "Поканата не е приета", + "authCreateAccount": "Създайте акаунт, за да започнете", + "authNoAccount": "Нямате акаунт?", "email": "Имейл", "password": "Парола", "confirmPassword": "Потвърждение на паролата", @@ -47,1479 +47,1479 @@ "edit": "Редактиране", "siteConfirmDelete": "Потвърждение на изтриване на сайта", "siteDelete": "Изтриване на сайта", - "siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.", - "siteMessageConfirm": "To confirm, please type the name of the site below.", - "siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?", - "siteManageSites": "Manage Sites", - "siteDescription": "Allow connectivity to your network through secure tunnels", - "siteCreate": "Create Site", - "siteCreateDescription2": "Follow the steps below to create and connect a new site", - "siteCreateDescription": "Create a new site to start connecting your resources", - "close": "Close", - "siteErrorCreate": "Error creating site", - "siteErrorCreateKeyPair": "Key pair or site defaults not found", - "siteErrorCreateDefaults": "Site defaults not found", - "method": "Method", - "siteMethodDescription": "This is how you will expose connections.", - "siteLearnNewt": "Learn how to install Newt on your system", - "siteSeeConfigOnce": "You will only be able to see the configuration once.", - "siteLoadWGConfig": "Loading WireGuard configuration...", - "siteDocker": "Expand for Docker Deployment Details", - "toggle": "Toggle", + "siteMessageRemove": "След изтриване, сайтът няма повече да бъде достъпен. Всички ресурси и цели, свързани със сайта, също ще бъдат премахнати.", + "siteMessageConfirm": "За потвърждение, моля, напишете името на сайта по-долу.", + "siteQuestionRemove": "Сигурни ли сте, че искате да премахнете сайта {selectedSite} от организацията?", + "siteManageSites": "Управление на сайтове", + "siteDescription": "Позволете свързване към вашата мрежа чрез сигурни тунели", + "siteCreate": "Създайте сайт", + "siteCreateDescription2": "Следвайте стъпките по-долу, за да създадете и свържете нов сайт", + "siteCreateDescription": "Създайте нов сайт, за да започнете да свързвате вашите ресурси", + "close": "Затвори", + "siteErrorCreate": "Грешка при създаване на сайт", + "siteErrorCreateKeyPair": "Ключова двойка или настройки по подразбиране на сайта не са намерени", + "siteErrorCreateDefaults": "Настройки по подразбиране на сайта не са намерени", + "method": "Метод", + "siteMethodDescription": "Това е как ще се изложат свързванията.", + "siteLearnNewt": "Научете как да инсталирате Newt на вашата система", + "siteSeeConfigOnce": "Ще можете да видите конфигурацията само веднъж.", + "siteLoadWGConfig": "Зареждане на WireGuard конфигурация...", + "siteDocker": "Разширете за детайли относно внедряване с Docker", + "toggle": "Превключване", "dockerCompose": "Docker Compose", "dockerRun": "Docker Run", - "siteLearnLocal": "Local sites do not tunnel, learn more", - "siteConfirmCopy": "I have copied the config", - "searchSitesProgress": "Search sites...", - "siteAdd": "Add Site", - "siteInstallNewt": "Install Newt", - "siteInstallNewtDescription": "Get Newt running on your system", - "WgConfiguration": "WireGuard Configuration", - "WgConfigurationDescription": "Use the following configuration to connect to your network", - "operatingSystem": "Operating System", - "commands": "Commands", - "recommended": "Recommended", - "siteNewtDescription": "For the best user experience, use Newt. It uses WireGuard under the hood and allows you to address your private resources by their LAN address on your private network from within the Pangolin dashboard.", - "siteRunsInDocker": "Runs in Docker", - "siteRunsInShell": "Runs in shell on macOS, Linux, and Windows", - "siteErrorDelete": "Error deleting site", - "siteErrorUpdate": "Failed to update site", - "siteErrorUpdateDescription": "An error occurred while updating the site.", + "siteLearnLocal": "Локалните сайтове не тунелират, научете повече", + "siteConfirmCopy": "Копирах конфигурацията", + "searchSitesProgress": "Търсене на сайтове...", + "siteAdd": "Добавете сайт", + "siteInstallNewt": "Инсталирайте Newt", + "siteInstallNewtDescription": "Пуснете Newt на вашата система", + "WgConfiguration": "WireGuard конфигурация", + "WgConfigurationDescription": "Използвайте следната конфигурация, за да се свържете с вашата мрежа", + "operatingSystem": "Операционна система", + "commands": "Команди", + "recommended": "Препоръчано", + "siteNewtDescription": "За най-добро потребителско преживяване, използвайте Newt. Това е WireoGuard под повърхността и ви позволява да осъществявате достъп до личните си ресурси чрез LAN адреса им от вашия частен Pangolin дашборд.", + "siteRunsInDocker": "Работи в Docker", + "siteRunsInShell": "Работи в обвивка на macOS, Linux и Windows", + "siteErrorDelete": "Грешка при изтриване на сайта", + "siteErrorUpdate": "Неуспешно актуализиране на сайта", + "siteErrorUpdateDescription": "Възникна грешка при актуализирането на сайта.", "siteUpdated": "Сайтът е обновен", - "siteUpdatedDescription": "The site has been updated.", - "siteGeneralDescription": "Configure the general settings for this site", - "siteSettingDescription": "Configure the settings on your site", - "siteSetting": "{siteName} Settings", - "siteNewtTunnel": "Newt Tunnel (Recommended)", - "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", - "siteWg": "Basic WireGuard", - "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES", - "siteLocalDescription": "Local resources only. No tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling. ONLY WORKS ON SELF HOSTED NODES", - "siteSeeAll": "See All Sites", - "siteTunnelDescription": "Determine how you want to connect to your site", - "siteNewtCredentials": "Newt Credentials", - "siteNewtCredentialsDescription": "This is how Newt will authenticate with the server", - "siteCredentialsSave": "Save Your Credentials", - "siteCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "siteInfo": "Site Information", - "status": "Status", - "shareTitle": "Manage Share Links", - "shareDescription": "Create shareable links to grant temporary or permanent access to your resources", - "shareSearch": "Search share links...", - "shareCreate": "Create Share Link", - "shareErrorDelete": "Failed to delete link", - "shareErrorDeleteMessage": "An error occurred deleting link", - "shareDeleted": "Link deleted", - "shareDeletedDescription": "The link has been deleted", - "shareTokenDescription": "Your access token can be passed in two ways: as a query parameter or in the request headers. These must be passed from the client on every request for authenticated access.", - "accessToken": "Access Token", - "usageExamples": "Usage Examples", - "tokenId": "Token ID", - "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", - "importantNote": "Important Note", - "shareImportantDescription": "For security reasons, using headers is recommended over query parameters when possible, as query parameters may be logged in server logs or browser history.", - "token": "Token", - "shareTokenSecurety": "Keep your access token secure. Do not share it in publicly accessible areas or client-side code.", - "shareErrorFetchResource": "Failed to fetch resources", - "shareErrorFetchResourceDescription": "An error occurred while fetching the resources", - "shareErrorCreate": "Failed to create share link", - "shareErrorCreateDescription": "An error occurred while creating the share link", - "shareCreateDescription": "Anyone with this link can access the resource", - "shareTitleOptional": "Title (optional)", - "expireIn": "Expire In", - "neverExpire": "Never expire", - "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", - "shareSeeOnce": "You will only be able to see this linkonce. Make sure to copy it.", - "shareAccessHint": "Anyone with this link can access the resource. Share it with care.", - "shareTokenUsage": "See Access Token Usage", - "createLink": "Create Link", - "resourcesNotFound": "No resources found", - "resourceSearch": "Search resources", - "openMenu": "Open menu", - "resource": "Resource", - "title": "Title", - "created": "Created", - "expires": "Expires", - "never": "Never", - "shareErrorSelectResource": "Please select a resource", - "resourceTitle": "Manage Resources", - "resourceDescription": "Create secure proxies to your private applications", - "resourcesSearch": "Search resources...", - "resourceAdd": "Add Resource", - "resourceErrorDelte": "Error deleting resource", - "authentication": "Authentication", - "protected": "Protected", - "notProtected": "Not Protected", - "resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.", - "resourceMessageConfirm": "To confirm, please type the name of the resource below.", - "resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?", - "resourceHTTP": "HTTPS Resource", - "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", - "resourceRaw": "Raw TCP/UDP Resource", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", - "resourceCreate": "Create Resource", - "resourceCreateDescription": "Follow the steps below to create a new resource", - "resourceSeeAll": "See All Resources", - "resourceInfo": "Resource Information", - "resourceNameDescription": "This is the display name for the resource.", - "siteSelect": "Select site", - "siteSearch": "Search site", - "siteNotFound": "No site found.", - "siteSelectionDescription": "This site will provide connectivity to the target.", - "resourceType": "Resource Type", - "resourceTypeDescription": "Determine how you want to access your resource", - "resourceHTTPSSettings": "HTTPS Settings", - "resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS", - "domainType": "Domain Type", - "subdomain": "Subdomain", - "baseDomain": "Base Domain", - "subdomnainDescription": "The subdomain where your resource will be accessible.", - "resourceRawSettings": "TCP/UDP Settings", - "resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP", - "protocol": "Protocol", - "protocolSelect": "Select a protocol", - "resourcePortNumber": "Port Number", - "resourcePortNumberDescription": "The external port number to proxy requests.", - "cancel": "Cancel", - "resourceConfig": "Configuration Snippets", - "resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource", - "resourceAddEntrypoints": "Traefik: Add Entrypoints", - "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", - "resourceLearnRaw": "Learn how to configure TCP/UDP resources", - "resourceBack": "Back to Resources", - "resourceGoTo": "Go to Resource", - "resourceDelete": "Delete Resource", - "resourceDeleteConfirm": "Confirm Delete Resource", - "visibility": "Visibility", - "enabled": "Enabled", - "disabled": "Disabled", - "general": "General", - "generalSettings": "General Settings", - "proxy": "Proxy", - "internal": "Internal", - "rules": "Rules", - "resourceSettingDescription": "Configure the settings on your resource", - "resourceSetting": "{resourceName} Settings", - "alwaysAllow": "Always Allow", - "alwaysDeny": "Always Deny", - "passToAuth": "Pass to Auth", - "orgSettingsDescription": "Configure your organization's general settings", - "orgGeneralSettings": "Organization Settings", - "orgGeneralSettingsDescription": "Manage your organization details and configuration", - "saveGeneralSettings": "Save General Settings", - "saveSettings": "Save Settings", - "orgDangerZone": "Danger Zone", - "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", - "orgDelete": "Delete Organization", - "orgDeleteConfirm": "Confirm Delete Organization", - "orgMessageRemove": "This action is irreversible and will delete all associated data.", - "orgMessageConfirm": "To confirm, please type the name of the organization below.", - "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", - "orgUpdated": "Organization updated", - "orgUpdatedDescription": "The organization has been updated.", - "orgErrorUpdate": "Failed to update organization", - "orgErrorUpdateMessage": "An error occurred while updating the organization.", - "orgErrorFetch": "Failed to fetch organizations", - "orgErrorFetchMessage": "An error occurred while listing your organizations", - "orgErrorDelete": "Failed to delete organization", - "orgErrorDeleteMessage": "An error occurred while deleting the organization.", - "orgDeleted": "Organization deleted", - "orgDeletedMessage": "The organization and its data has been deleted.", - "orgMissing": "Organization ID Missing", - "orgMissingMessage": "Unable to regenerate invitation without an organization ID.", - "accessUsersManage": "Manage Users", - "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", - "accessUsersSearch": "Search users...", - "accessUserCreate": "Create User", - "accessUserRemove": "Remove User", - "username": "Username", - "identityProvider": "Identity Provider", - "role": "Role", - "nameRequired": "Name is required", - "accessRolesManage": "Manage Roles", - "accessRolesDescription": "Configure roles to manage access to your organization", - "accessRolesSearch": "Search roles...", - "accessRolesAdd": "Add Role", - "accessRoleDelete": "Delete Role", - "description": "Description", - "inviteTitle": "Open Invitations", - "inviteDescription": "Manage your invitations to other users", - "inviteSearch": "Search invitations...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years", - "day": "{count, plural, one {# day} other {# days}}", - "apiKeysTitle": "API Key Information", - "apiKeysConfirmCopy2": "You must confirm that you have copied the API key.", - "apiKeysErrorCreate": "Error creating API key", - "apiKeysErrorSetPermission": "Error setting permissions", - "apiKeysCreate": "Generate API Key", - "apiKeysCreateDescription": "Generate a new API key for your organization", - "apiKeysGeneralSettings": "Permissions", - "apiKeysGeneralSettingsDescription": "Determine what this API key can do", - "apiKeysList": "Your API Key", - "apiKeysSave": "Save Your API Key", - "apiKeysSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "apiKeysInfo": "Your API key is:", - "apiKeysConfirmCopy": "I have copied the API key", - "generate": "Generate", - "done": "Done", - "apiKeysSeeAll": "See All API Keys", - "apiKeysPermissionsErrorLoadingActions": "Error loading API key actions", - "apiKeysPermissionsErrorUpdate": "Error setting permissions", - "apiKeysPermissionsUpdated": "Permissions updated", - "apiKeysPermissionsUpdatedDescription": "The permissions have been updated.", - "apiKeysPermissionsGeneralSettings": "Permissions", - "apiKeysPermissionsGeneralSettingsDescription": "Determine what this API key can do", - "apiKeysPermissionsSave": "Save Permissions", - "apiKeysPermissionsTitle": "Permissions", - "apiKeys": "API Keys", - "searchApiKeys": "Search API keys...", - "apiKeysAdd": "Generate API Key", - "apiKeysErrorDelete": "Error deleting API key", - "apiKeysErrorDeleteMessage": "Error deleting API key", - "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", - "apiKeysMessageRemove": "Once removed, the API key will no longer be able to be used.", - "apiKeysMessageConfirm": "To confirm, please type the name of the API key below.", - "apiKeysDeleteConfirm": "Confirm Delete API Key", - "apiKeysDelete": "Delete API Key", - "apiKeysManage": "Manage API Keys", - "apiKeysDescription": "API keys are used to authenticate with the integration API", - "apiKeysSettings": "{apiKeyName} Settings", - "userTitle": "Manage All Users", - "userDescription": "View and manage all users in the system", - "userAbount": "About User Management", - "userAbountDescription": "This table displays all root user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their root user object - they will remain in the system. To completely remove a user from the system, you must delete their root user object using the delete action in this table.", - "userServer": "Server Users", - "userSearch": "Search server users...", - "userErrorDelete": "Error deleting user", - "userDeleteConfirm": "Confirm Delete User", - "userDeleteServer": "Delete User from Server", - "userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.", - "userMessageConfirm": "To confirm, please type the name of the user below.", - "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", - "licenseKey": "License Key", - "valid": "Valid", - "numberOfSites": "Number of Sites", - "licenseKeySearch": "Search license keys...", - "licenseKeyAdd": "Add License Key", - "type": "Type", - "licenseKeyRequired": "License key is required", - "licenseTermsAgree": "You must agree to the license terms", - "licenseErrorKeyLoad": "Failed to load license keys", - "licenseErrorKeyLoadDescription": "An error occurred loading license keys.", - "licenseErrorKeyDelete": "Failed to delete license key", - "licenseErrorKeyDeleteDescription": "An error occurred deleting license key.", - "licenseKeyDeleted": "License key deleted", - "licenseKeyDeletedDescription": "The license key has been deleted.", - "licenseErrorKeyActivate": "Failed to activate license key", - "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", - "licenseAbout": "About Licensing", - "communityEdition": "Community Edition", - "licenseAboutDescription": "This is for business and enterprise users who are using Pangolin in a commercial environment. If you are using Pangolin for personal use, you can ignore this section.", - "licenseKeyActivated": "License key activated", - "licenseKeyActivatedDescription": "The license key has been successfully activated.", - "licenseErrorKeyRecheck": "Failed to recheck license keys", - "licenseErrorKeyRecheckDescription": "An error occurred rechecking license keys.", - "licenseErrorKeyRechecked": "License keys rechecked", - "licenseErrorKeyRecheckedDescription": "All license keys have been rechecked", - "licenseActivateKey": "Activate License Key", - "licenseActivateKeyDescription": "Enter a license key to activate it.", - "licenseActivate": "Activate License", - "licenseAgreement": "By checking this box, you confirm that you have read and agree to the license terms corresponding to the tier associated with your license key.", - "fossorialLicense": "View Fossorial Commercial License & Subscription Terms", - "licenseMessageRemove": "This will remove the license key and all associated permissions granted by it.", - "licenseMessageConfirm": "To confirm, please type the license key below.", - "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", - "licenseKeyDelete": "Delete License Key", - "licenseKeyDeleteConfirm": "Confirm Delete License Key", - "licenseTitle": "Manage License Status", - "licenseTitleDescription": "View and manage license keys in the system", - "licenseHost": "Host License", - "licenseHostDescription": "Manage the main license key for the host.", - "licensedNot": "Not Licensed", - "hostId": "Host ID", - "licenseReckeckAll": "Recheck All Keys", - "licenseSiteUsage": "Sites Usage", - "licenseSiteUsageDecsription": "View the number of sites using this license.", - "licenseNoSiteLimit": "There is no limit on the number of sites using an unlicensed host.", - "licensePurchase": "Purchase License", - "licensePurchaseSites": "Purchase Additional Sites", - "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", - "licenseSitesUsed": "{count, plural, =0 {# sites} one {# site} other {# sites}} in system.", - "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", - "licenseFee": "License fee", - "licensePriceSite": "Price per site", - "total": "Total", - "licenseContinuePayment": "Continue to Payment", - "pricingPage": "pricing page", - "pricingPortal": "See Purchase Portal", - "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", - "inviteRemove": "Remove Invitation", - "inviteRemoveError": "Failed to remove invitation", - "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", - "inviteRemoved": "Invitation removed", - "inviteRemovedDescription": "The invitation for {email} has been removed.", - "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", - "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", - "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", - "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", - "inviteRemoveConfirm": "Confirm Remove Invitation", - "inviteRegenerated": "Invitation Regenerated", - "inviteSent": "A new invitation has been sent to {email}.", - "inviteSentEmail": "Send email notification to the user", - "inviteGenerate": "A new invitation has been generated for {email}.", - "inviteDuplicateError": "Duplicate Invite", - "inviteDuplicateErrorDescription": "An invitation for this user already exists.", - "inviteRateLimitError": "Rate Limit Exceeded", - "inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.", - "inviteRegenerateError": "Failed to Regenerate Invitation", - "inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.", - "inviteValidityPeriod": "Validity Period", - "inviteValidityPeriodSelect": "Select validity period", - "inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.", - "inviteRegenerateButton": "Regenerate", - "expiresAt": "Expires At", - "accessRoleUnknown": "Unknown Role", - "placeholder": "Placeholder", - "userErrorOrgRemove": "Failed to remove user", - "userErrorOrgRemoveDescription": "An error occurred while removing the user.", - "userOrgRemoved": "User removed", - "userOrgRemovedDescription": "The user {email} has been removed from the organization.", - "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", - "userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.", - "userMessageOrgConfirm": "To confirm, please type the name of the of the user below.", - "userRemoveOrgConfirm": "Confirm Remove User", - "userRemoveOrg": "Remove User from Organization", - "users": "Users", - "accessRoleMember": "Member", - "accessRoleOwner": "Owner", - "userConfirmed": "Confirmed", - "idpNameInternal": "Internal", - "emailInvalid": "Invalid email address", - "inviteValidityDuration": "Please select a duration", - "accessRoleSelectPlease": "Please select a role", - "usernameRequired": "Username is required", - "idpSelectPlease": "Please select an identity provider", - "idpGenericOidc": "Generic OAuth2/OIDC provider.", - "accessRoleErrorFetch": "Failed to fetch roles", - "accessRoleErrorFetchDescription": "An error occurred while fetching the roles", - "idpErrorFetch": "Failed to fetch identity providers", - "idpErrorFetchDescription": "An error occurred while fetching identity providers", - "userErrorExists": "User Already Exists", - "userErrorExistsDescription": "This user is already a member of the organization.", - "inviteError": "Failed to invite user", - "inviteErrorDescription": "An error occurred while inviting the user", - "userInvited": "User invited", - "userInvitedDescription": "The user has been successfully invited.", - "userErrorCreate": "Failed to create user", - "userErrorCreateDescription": "An error occurred while creating the user", - "userCreated": "User created", - "userCreatedDescription": "The user has been successfully created.", - "userTypeInternal": "Internal User", - "userTypeInternalDescription": "Invite a user to join your organization directly.", - "userTypeExternal": "External User", - "userTypeExternalDescription": "Create a user with an external identity provider.", - "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", - "userSettings": "User Information", - "userSettingsDescription": "Enter the details for the new user", - "inviteEmailSent": "Send invite email to user", - "inviteValid": "Valid For", - "selectDuration": "Select duration", - "accessRoleSelect": "Select role", - "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", - "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", - "inviteExpiresIn": "The invite will expire in {days, plural, one {# day} other {# days}}.", - "idpTitle": "Identity Provider", - "idpSelect": "Select the identity provider for the external user", - "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", - "usernameUniq": "This must match the unique username that exists in the selected identity provider.", - "emailOptional": "Email (Optional)", - "nameOptional": "Name (Optional)", - "accessControls": "Access Controls", - "userDescription2": "Manage the settings on this user", - "accessRoleErrorAdd": "Failed to add user to role", - "accessRoleErrorAddDescription": "An error occurred while adding user to the role.", - "userSaved": "User saved", - "userSavedDescription": "The user has been updated.", - "autoProvisioned": "Auto Provisioned", - "autoProvisionedDescription": "Allow this user to be automatically managed by identity provider", - "accessControlsDescription": "Manage what this user can access and do in the organization", - "accessControlsSubmit": "Save Access Controls", - "roles": "Roles", - "accessUsersRoles": "Manage Users & Roles", - "accessUsersRolesDescription": "Invite users and add them to roles to manage access to your organization", - "key": "Key", - "createdAt": "Created At", - "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", - "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", - "proxyEnableSSL": "Enable SSL (https)", - "targetErrorFetch": "Failed to fetch targets", - "targetErrorFetchDescription": "An error occurred while fetching targets", - "siteErrorFetch": "Failed to fetch resource", - "siteErrorFetchDescription": "An error occurred while fetching resource", - "targetErrorDuplicate": "Duplicate target", - "targetErrorDuplicateDescription": "A target with these settings already exists", - "targetWireGuardErrorInvalidIp": "Invalid target IP", - "targetWireGuardErrorInvalidIpDescription": "Target IP must be within the site subnet", - "targetsUpdated": "Targets updated", - "targetsUpdatedDescription": "Targets and settings updated successfully", - "targetsErrorUpdate": "Failed to update targets", - "targetsErrorUpdateDescription": "An error occurred while updating targets", - "targetTlsUpdate": "TLS settings updated", - "targetTlsUpdateDescription": "Your TLS settings have been updated successfully", - "targetErrorTlsUpdate": "Failed to update TLS settings", - "targetErrorTlsUpdateDescription": "An error occurred while updating TLS settings", - "proxyUpdated": "Proxy settings updated", - "proxyUpdatedDescription": "Your proxy settings have been updated successfully", - "proxyErrorUpdate": "Failed to update proxy settings", - "proxyErrorUpdateDescription": "An error occurred while updating proxy settings", - "targetAddr": "IP / Hostname", - "targetPort": "Port", - "targetProtocol": "Protocol", - "targetTlsSettings": "Secure Connection Configuration", - "targetTlsSettingsDescription": "Configure SSL/TLS settings for your resource", - "targetTlsSettingsAdvanced": "Advanced TLS Settings", - "targetTlsSni": "TLS Server Name (SNI)", - "targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.", - "targetTlsSubmit": "Save Settings", - "targets": "Targets Configuration", - "targetsDescription": "Set up targets to route traffic to your backend services", - "targetStickySessions": "Enable Sticky Sessions", - "targetStickySessionsDescription": "Keep connections on the same backend target for their entire session.", - "methodSelect": "Select method", - "targetSubmit": "Add Target", - "targetNoOne": "No targets. Add a target using the form.", - "targetNoOneDescription": "Adding more than one target above will enable load balancing.", - "targetsSubmit": "Save Targets", - "proxyAdditional": "Additional Proxy Settings", - "proxyAdditionalDescription": "Configure how your resource handles proxy settings", - "proxyCustomHeader": "Custom Host Header", - "proxyCustomHeaderDescription": "The host header to set when proxying requests. Leave empty to use the default.", - "proxyAdditionalSubmit": "Save Proxy Settings", - "subnetMaskErrorInvalid": "Invalid subnet mask. Must be between 0 and 32.", - "ipAddressErrorInvalidFormat": "Invalid IP address format", - "ipAddressErrorInvalidOctet": "Invalid IP address octet", - "path": "Path", - "matchPath": "Match Path", - "ipAddressRange": "IP Range", - "rulesErrorFetch": "Failed to fetch rules", - "rulesErrorFetchDescription": "An error occurred while fetching rules", - "rulesErrorDuplicate": "Duplicate rule", - "rulesErrorDuplicateDescription": "A rule with these settings already exists", - "rulesErrorInvalidIpAddressRange": "Invalid CIDR", - "rulesErrorInvalidIpAddressRangeDescription": "Please enter a valid CIDR value", - "rulesErrorInvalidUrl": "Invalid URL path", - "rulesErrorInvalidUrlDescription": "Please enter a valid URL path value", - "rulesErrorInvalidIpAddress": "Invalid IP", - "rulesErrorInvalidIpAddressDescription": "Please enter a valid IP address", - "rulesErrorUpdate": "Failed to update rules", - "rulesErrorUpdateDescription": "An error occurred while updating rules", - "rulesUpdated": "Enable Rules", - "rulesUpdatedDescription": "Rule evaluation has been updated", - "rulesMatchIpAddressRangeDescription": "Enter an address in CIDR format (e.g., 103.21.244.0/22)", - "rulesMatchIpAddress": "Enter an IP address (e.g., 103.21.244.12)", - "rulesMatchUrl": "Enter a URL path or pattern (e.g., /api/v1/todos or /api/v1/*)", - "rulesErrorInvalidPriority": "Invalid Priority", - "rulesErrorInvalidPriorityDescription": "Please enter a valid priority", - "rulesErrorDuplicatePriority": "Duplicate Priorities", - "rulesErrorDuplicatePriorityDescription": "Please enter unique priorities", - "ruleUpdated": "Rules updated", - "ruleUpdatedDescription": "Rules updated successfully", - "ruleErrorUpdate": "Operation failed", - "ruleErrorUpdateDescription": "An error occurred during the save operation", - "rulesPriority": "Priority", - "rulesAction": "Action", - "rulesMatchType": "Match Type", - "value": "Value", - "rulesAbout": "About Rules", - "rulesAboutDescription": "Rules allow you to control access to your resource based on a set of criteria. You can create rules to allow or deny access based on IP address or URL path.", - "rulesActions": "Actions", - "rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods", - "rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted", - "rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted", - "rulesMatchCriteria": "Matching Criteria", - "rulesMatchCriteriaIpAddress": "Match a specific IP address", - "rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation", - "rulesMatchCriteriaUrl": "Match a URL path or pattern", - "rulesEnable": "Enable Rules", - "rulesEnableDescription": "Enable or disable rule evaluation for this resource", - "rulesResource": "Resource Rules Configuration", - "rulesResourceDescription": "Configure rules to control access to your resource", - "ruleSubmit": "Add Rule", - "rulesNoOne": "No rules. Add a rule using the form.", - "rulesOrder": "Rules are evaluated by priority in ascending order.", - "rulesSubmit": "Save Rules", - "resourceErrorCreate": "Error creating resource", - "resourceErrorCreateDescription": "An error occurred when creating the resource", - "resourceErrorCreateMessage": "Error creating resource:", - "resourceErrorCreateMessageDescription": "An unexpected error occurred", - "sitesErrorFetch": "Error fetching sites", - "sitesErrorFetchDescription": "An error occurred when fetching the sites", - "domainsErrorFetch": "Error fetching domains", - "domainsErrorFetchDescription": "An error occurred when fetching the domains", - "none": "None", - "unknown": "Unknown", - "resources": "Resources", - "resourcesDescription": "Resources are proxies to applications running on your private network. Create a resource for any HTTP/HTTPS or raw TCP/UDP service on your private network. Each resource must be connected to a site to enable private, secure connectivity through an encrypted WireGuard tunnel.", - "resourcesWireGuardConnect": "Secure connectivity with WireGuard encryption", - "resourcesMultipleAuthenticationMethods": "Configure multiple authentication methods", - "resourcesUsersRolesAccess": "User and role-based access control", - "resourcesErrorUpdate": "Failed to toggle resource", - "resourcesErrorUpdateDescription": "An error occurred while updating the resource", - "access": "Access", - "shareLink": "{resource} Share Link", - "resourceSelect": "Select resource", - "shareLinks": "Share Links", - "share": "Shareable Links", - "shareDescription2": "Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one.", - "shareEasyCreate": "Easy to create and share", - "shareConfigurableExpirationDuration": "Configurable expiration duration", - "shareSecureAndRevocable": "Secure and revocable", - "nameMin": "Name must be at least {len} characters.", - "nameMax": "Name must not be longer than {len} characters.", - "sitesConfirmCopy": "Please confirm that you have copied the config.", - "unknownCommand": "Unknown command", - "newtErrorFetchReleases": "Failed to fetch release info: {err}", - "newtErrorFetchLatest": "Error fetching latest release: {err}", - "newtEndpoint": "Newt Endpoint", + "siteUpdatedDescription": "Сайтът е актуализиран.", + "siteGeneralDescription": "Конфигурирайте общи настройки за този сайт", + "siteSettingDescription": "Настройте настройките на вашия сайт", + "siteSetting": "Настройки на {siteName}", + "siteNewtTunnel": "Newt тунел (Препоръчително)", + "siteNewtTunnelDescription": "Най-лесният начин да създадете входна точка в мрежата си. Без допълнително конфигуриране.", + "siteWg": "Основен WireGuard", + "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES", + "siteWgDescriptionSaas": "Използвайте всеки WireGuard клиент за установяване на тунел. Ръчно нат задаване е необходимо. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", + "siteLocalDescription": "Local resources only. No tunneling. ONLY WORKS ON SELF HOSTED NODES", + "siteLocalDescriptionSaas": "Само локални ресурси. Без тунелиране. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", + "siteSeeAll": "Вижте всички сайтове", + "siteTunnelDescription": "Определете как искате да се свържете с вашия сайт", + "siteNewtCredentials": "Newt Удостоверения", + "siteNewtCredentialsDescription": "Това е така, защото Newt ще се удостовери със сървъра", + "siteCredentialsSave": "Запазете вашите удостоверения", + "siteCredentialsSaveDescription": "Ще можете да виждате това само веднъж. Уверете се да го копирате на сигурно място.", + "siteInfo": "Информация за сайта", + "status": "Статус", + "shareTitle": "Управление на връзки за споделяне", + "shareDescription": "Създайте споделяеми връзки, за да разрешите временен или постоянен достъп до вашите ресурси", + "shareSearch": "Търсене на връзки за споделяне...", + "shareCreate": "Създайте връзка за споделяне", + "shareErrorDelete": "Неуспешно изтриване на връзката", + "shareErrorDeleteMessage": "Възникна грешка при изтриване на връзката", + "shareDeleted": "Връзката беше изтрита", + "shareDeletedDescription": "Връзката беше премахната", + "shareTokenDescription": "Вашият достъп токен може да се предава по два начина: като параметър на URL или в заглавките на заявката. Тези трябва да се предават от клиента при всяка заявка за удостоверен достъп.", + "accessToken": "Достъп Токен", + "usageExamples": "Примери за използване", + "tokenId": "Токен ID", + "requestHeades": "Заглавие на заявката", + "queryParameter": "Параметър за заявка", + "importantNote": "Важно бележка", + "shareImportantDescription": "По съображения за сигурност, използването на заглавки се препоръчва пред параметри на заявка, когато е възможно, тъй като параметри на заявка могат да бъдат записвани в логове на сървъра или в историята на браузъра.", + "token": "Токен", + "shareTokenSecurety": "Пазете вашият достъп токен в безопасност. Не го споделяйте в публичнодостъпни зони или клиентски код.", + "shareErrorFetchResource": "Неуспешно вземане на ресурси", + "shareErrorFetchResourceDescription": "Възникна грешка при опит за вземане на ресурсите", + "shareErrorCreate": "Неуспешно създаване на връзка за споделяне", + "shareErrorCreateDescription": "Възникна грешка при създаването на връзката за споделяне", + "shareCreateDescription": "Всеки с тази връзка може да получи достъп до ресурса", + "shareTitleOptional": "Заглавие (по избор)", + "expireIn": "Изтече", + "neverExpire": "Никога не изтича", + "shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.", + "shareSeeOnce": "Ще можете да видите тази връзка само веднъж. Уверете се да я копирате.", + "shareAccessHint": "Всеки с тази връзка може да има достъп до ресурса. Споделяйте я с внимание.", + "shareTokenUsage": "Вижте използването на токена за достъп", + "createLink": "Създаване на връзка", + "resourcesNotFound": "Не са намерени ресурси", + "resourceSearch": "Търсене на ресурси", + "openMenu": "Отваряне на менюто", + "resource": "Ресурс", + "title": "Заглавие", + "created": "Създадено", + "expires": "Изтича", + "never": "Никога", + "shareErrorSelectResource": "Моля, изберете ресурс", + "resourceTitle": "Управление на ресурси", + "resourceDescription": "Създайте сигурни проксита към вашите частни приложения", + "resourcesSearch": "Търсене на ресурси...", + "resourceAdd": "Добавете ресурс", + "resourceErrorDelte": "Грешка при изтриване на ресурс", + "authentication": "Удостоверяване", + "protected": "Защита", + "notProtected": "Не защитен", + "resourceMessageRemove": "След като се премахне, ресурсът няма повече да бъде достъпен. Всички цели, свързани с ресурса, също ще бъдат премахнати.", + "resourceMessageConfirm": "За потвърждение, моля, напишете името на ресурса по-долу.", + "resourceQuestionRemove": "Сигурни ли сте, че искате да премахнете ресурса {selectedResource} от организацията?", + "resourceHTTP": "HTTPS ресурс", + "resourceHTTPDescription": "Прокси заявки към вашето приложение през HTTPS с помощта на субдомейн или базов домейн.", + "resourceRaw": "Суров TCP/UDP ресурс", + "resourceRawDescription": "Прокси заявки към вашето приложение през TCP/UDP с помощта на номер на порт.", + "resourceCreate": "Създайте ресурс", + "resourceCreateDescription": "Следвайте стъпките по-долу, за да създадете нов ресурс", + "resourceSeeAll": "Вижте всички ресурси", + "resourceInfo": "Информация за ресурса", + "resourceNameDescription": "Това е дисплейното име на ресурса.", + "siteSelect": "Изберете сайт", + "siteSearch": "Търсене на сайт", + "siteNotFound": "Няма намерени сайтове.", + "siteSelectionDescription": "Този сайт ще осигури свързаност до целта.", + "resourceType": "Тип ресурс", + "resourceTypeDescription": "Определете как искате да получите достъп до вашия ресурс", + "resourceHTTPSSettings": "HTTPS настройки", + "resourceHTTPSSettingsDescription": "Конфигурирайте как вашият ресурс ще бъде достъпен през HTTPS", + "domainType": "Тип домейн", + "subdomain": "Субдомейн", + "baseDomain": "Базов домейн", + "subdomnainDescription": "Субдомейнът, в който ще бъде достъпен вашият ресурс.", + "resourceRawSettings": "TCP/UDP настройки", + "resourceRawSettingsDescription": "Конфигурирайте как вашият ресурс ще бъде достъпен през TCP/UDP", + "protocol": "Протокол", + "protocolSelect": "Изберете протокол", + "resourcePortNumber": "Номер на порт", + "resourcePortNumberDescription": "Външен номер на порт за прокси заявки.", + "cancel": "Отмяна", + "resourceConfig": "Конфигурационни фрагменти", + "resourceConfigDescription": "Копирайте и поставете тези конфигурационни фрагменти за настройка на вашия TCP/UDP ресурс", + "resourceAddEntrypoints": "Traefik: Добавете Входни точки", + "resourceExposePorts": "Gerbil: Изложете портове в Docker Compose", + "resourceLearnRaw": "Научете как да конфигурирате TCP/UDP ресурси", + "resourceBack": "Назад към ресурсите", + "resourceGoTo": "Отидете към ресурса", + "resourceDelete": "Изтрийте ресурс", + "resourceDeleteConfirm": "Потвърдете изтриване на ресурс", + "visibility": "Видимост", + "enabled": "Активиран", + "disabled": "Деактивиран", + "general": "Общи", + "generalSettings": "Общи настройки", + "proxy": "Прокси", + "internal": "Вътрешен", + "rules": "Правила", + "resourceSettingDescription": "Конфигурирайте настройките на вашия ресурс", + "resourceSetting": "Настройки на {resourceName}", + "alwaysAllow": "Винаги позволявай", + "alwaysDeny": "Винаги отказвай", + "passToAuth": "Прехвърляне към удостоверяване", + "orgSettingsDescription": "Конфигурирайте общите настройки на вашата организация", + "orgGeneralSettings": "Настройки на организацията", + "orgGeneralSettingsDescription": "Управлявайте детайлите и конфигурацията на вашата организация", + "saveGeneralSettings": "Запазете общите настройки", + "saveSettings": "Запазване на настройките", + "orgDangerZone": "Опасна зона", + "orgDangerZoneDescription": "След като изтриете тази организация, няма връщане назад. Моля, бъдете сигурен.", + "orgDelete": "Изтрийте организацията", + "orgDeleteConfirm": "Потвърдете изтриване на организация", + "orgMessageRemove": "Това действие е необратимо и ще изтрие всички свързани данни.", + "orgMessageConfirm": "За потвърждение, моля, напишете името на организацията по-долу.", + "orgQuestionRemove": "Сигурни ли сте, че искате да премахнете организацията {selectedOrg}?", + "orgUpdated": "Организацията е актуализирана", + "orgUpdatedDescription": "Организацията е обновена.", + "orgErrorUpdate": "Неуспешно актуализиране на организацията", + "orgErrorUpdateMessage": "Възникна грешка при актуализиране на организацията.", + "orgErrorFetch": "Неуспешно вземане на организации", + "orgErrorFetchMessage": "Възникна грешка при изброяване на вашите организации", + "orgErrorDelete": "Неуспешно изтриване на организацията", + "orgErrorDeleteMessage": "Възникна грешка при изтриването на организацията.", + "orgDeleted": "Организацията е изтрита", + "orgDeletedMessage": "Организацията и нейните данни са изтрити.", + "orgMissing": "Липсва идентификатор на организация", + "orgMissingMessage": "Невъзможност за регенериране на покана без идентификатор на организация.", + "accessUsersManage": "Управление на потребители", + "accessUsersDescription": "Поканете потребители и ги добавете в роли, за да управлявате достъпа до вашата организация", + "accessUsersSearch": "Търсене на потребители...", + "accessUserCreate": "Създайте потребител", + "accessUserRemove": "Премахнете потребител", + "username": "Потребителско име", + "identityProvider": "Доставчик на идентичност", + "role": "Роля", + "nameRequired": "Името е задължително", + "accessRolesManage": "Управление на роли", + "accessRolesDescription": "Конфигурирайте роли, за да управлявате достъпа до вашата организация", + "accessRolesSearch": "Търсене на роли...", + "accessRolesAdd": "Добавете роля", + "accessRoleDelete": "Изтриване на роля", + "description": "Описание", + "inviteTitle": "Отворени покани", + "inviteDescription": "Управление на вашите покани към други потребители", + "inviteSearch": "Търсене на покани...", + "minutes": "Минути", + "hours": "Часове", + "days": "Дни", + "weeks": "Седмици", + "months": "Месеци", + "years": "Години", + "day": "{count, plural, one {# ден} other {# дни}}", + "apiKeysTitle": "Информация за API ключ", + "apiKeysConfirmCopy2": "Трябва да потвърдите, че сте копирали API ключът.", + "apiKeysErrorCreate": "Грешка при създаване на API ключ", + "apiKeysErrorSetPermission": "Грешка при задаване на разрешения", + "apiKeysCreate": "Генерирайте API ключ", + "apiKeysCreateDescription": "Генерирайте нов API ключ за вашата организация", + "apiKeysGeneralSettings": "Разрешения", + "apiKeysGeneralSettingsDescription": "Определете какво може да прави този API ключ", + "apiKeysList": "Вашият API ключ", + "apiKeysSave": "Запазване на вашия API ключ", + "apiKeysSaveDescription": "Ще можете да виждате това само веднъж. Уверете се да го копирате на сигурно място.", + "apiKeysInfo": "Вашият API ключ е:", + "apiKeysConfirmCopy": "Копирах API ключа", + "generate": "Генериране", + "done": "Готово", + "apiKeysSeeAll": "Вижте всички API ключове", + "apiKeysPermissionsErrorLoadingActions": "Грешка при зареждане на действията на API ключа", + "apiKeysPermissionsErrorUpdate": "Грешка при задаване на разрешения", + "apiKeysPermissionsUpdated": "Разрешенията са актуализирани", + "apiKeysPermissionsUpdatedDescription": "Разрешенията са обновени.", + "apiKeysPermissionsGeneralSettings": "Разрешения", + "apiKeysPermissionsGeneralSettingsDescription": "Определете какво може да прави този API ключ", + "apiKeysPermissionsSave": "Запазете разрешенията", + "apiKeysPermissionsTitle": "Разрешения", + "apiKeys": "API ключове", + "searchApiKeys": "Търсене на API ключове...", + "apiKeysAdd": "Генерирайте API ключ", + "apiKeysErrorDelete": "Грешка при изтриване на API ключ", + "apiKeysErrorDeleteMessage": "Грешка при изтриване на API ключ", + "apiKeysQuestionRemove": "Сигурни ли сте, че искате да премахнете API ключа {selectedApiKey} от организацията?", + "apiKeysMessageRemove": "След като бъде премахнат, API ключът няма вече да може да се използва.", + "apiKeysMessageConfirm": "За потвърждение, моля, напишете името на API ключа по-долу.", + "apiKeysDeleteConfirm": "Потвърдете изтриване на API ключ", + "apiKeysDelete": "Изтрийте API ключа", + "apiKeysManage": "Управление на API ключове", + "apiKeysDescription": "API ключове се използват за удостоверяване с интеграционния API", + "apiKeysSettings": "Настройки на {apiKeyName}", + "userTitle": "Управление на всички потребители", + "userDescription": "Преглед и управление на всички потребители в системата", + "userAbount": "Относно управлението на потребители", + "userAbountDescription": "Тази таблица показва всички рут потребителски обекти в системата. Всеки потребител може да принадлежи към множество организации. Изтриването на потребител от организация не премахва неговия рут потребителски обект - той ще остане в системата. За да премахнете напълно потребител от системата, трябва да изтриете неговия рут потребителски обект чрез действие за изтриване в тази таблица.", + "userServer": "Сървърни потребители", + "userSearch": "Търсене на сървърни потребители...", + "userErrorDelete": "Грешка при изтриване на потребител", + "userDeleteConfirm": "Потвърдете изтриването на потребител", + "userDeleteServer": "Изтрийте потребителя от сървъра", + "userMessageRemove": "Потребителят ще бъде премахнат от всички организации и напълно заличен от сървъра.", + "userMessageConfirm": "За да потвърдите, въведете името на потребителя по-долу.", + "userQuestionRemove": "Сигурни ли сте, че искате да изтриете завинаги {selectedUser} от сървъра?", + "licenseKey": "Ключ за лиценз", + "valid": "Валиден", + "numberOfSites": "Брой сайтове", + "licenseKeySearch": "Търсене на лицензионни ключове...", + "licenseKeyAdd": "Добавете лицензионен ключ", + "type": "Тип", + "licenseKeyRequired": "Необходим е лицензионен ключ", + "licenseTermsAgree": "Трябва да се съгласите с лицензионните условия", + "licenseErrorKeyLoad": "Неуспешно зареждане на лицензионни ключове", + "licenseErrorKeyLoadDescription": "Възникна грешка при зареждане на лицензионните ключове.", + "licenseErrorKeyDelete": "Неуспешно изтриване на лицензионен ключ", + "licenseErrorKeyDeleteDescription": "Възникна грешка при изтриване на лицензионния ключ.", + "licenseKeyDeleted": "Лицензионният ключ е изтрит", + "licenseKeyDeletedDescription": "Лицензионният ключ беше изтрит.", + "licenseErrorKeyActivate": "Неуспешно активиране на лицензионния ключ", + "licenseErrorKeyActivateDescription": "Възникна грешка при активирането на лицензионния ключ.", + "licenseAbout": "Относно лицензите", + "communityEdition": "Комюнити издание", + "licenseAboutDescription": "Това е за бизнес и корпоративни потребители, които използват Pangolin в търговска среда. Ако използвате Pangolin за лична употреба, можете да игнорирате този раздел.", + "licenseKeyActivated": "Лицензионният ключ е активиран", + "licenseKeyActivatedDescription": "Лицензионният ключ беше успешно активиран.", + "licenseErrorKeyRecheck": "Неуспешно повторно проверяване на лицензионните ключове", + "licenseErrorKeyRecheckDescription": "Възникна грешка при повторно проверяване на лицензионните ключове.", + "licenseErrorKeyRechecked": "Лицензионните ключове бяха повторно проверени", + "licenseErrorKeyRecheckedDescription": "Всички лицензионни ключове бяха повторно проверени", + "licenseActivateKey": "Активиране на лицензионен ключ", + "licenseActivateKeyDescription": "Въведете лицензионен ключ, за да го активирате.", + "licenseActivate": "Активиране на лицензията", + "licenseAgreement": "Чрез поставяне на отметка в това поле потвърждавате, че сте прочели и се съгласявате с лицензионните условия, съответстващи на нивото, свързано с Вашия лицензионен ключ.", + "fossorialLicense": "Преглед на търговски условия и абонамент за Fossorial", + "licenseMessageRemove": "Това ще премахне лицензионния ключ и всички свързани права, предоставени от него.", + "licenseMessageConfirm": "За да потвърдите, въведете лицензионния ключ по-долу.", + "licenseQuestionRemove": "Сигурни ли сте, че искате да изтриете лицензионния ключ {selectedKey}?", + "licenseKeyDelete": "Изтриване на лицензионен ключ", + "licenseKeyDeleteConfirm": "Потвърдете изтриването на лицензионен ключ", + "licenseTitle": "Управление на лицензионния статус", + "licenseTitleDescription": "Преглед и управление на лицензионни ключове в системата", + "licenseHost": "Лиценз за хост", + "licenseHostDescription": "Управление на главния лицензионен ключ за хоста.", + "licensedNot": "Не е лицензиран", + "hostId": "Идентификатор на хост", + "licenseReckeckAll": "Повторно проверяване на всички ключове", + "licenseSiteUsage": "Използване на сайтове", + "licenseSiteUsageDecsription": "Преглед на броя на сайтовете, които използват този лиценз.", + "licenseNoSiteLimit": "Няма лимит за броя на сайтовете, използващи нелицензиран хост.", + "licensePurchase": "Закупуване на лиценз", + "licensePurchaseSites": "Закупуване на допълнителни сайтове", + "licenseSitesUsedMax": "{usedSites} от {maxSites} сайтове използвани", + "licenseSitesUsed": "{count, plural, =0 {# сайта} one {# сайт} other {# сайта}} в системата.", + "licensePurchaseDescription": "Изберете колко сайтове искате да {selectedMode, select, license {закупите лиценз за. Можете винаги да добавите повече сайтове по-късно.} other {добавите към съществуващия си лиценз.}}", + "licenseFee": "Такса за лиценз", + "licensePriceSite": "Цена на сайт", + "total": "Общо", + "licenseContinuePayment": "Продължете към плащане", + "pricingPage": "страница с цени", + "pricingPortal": "Преглед на портала за закупуване", + "licensePricingPage": "За най-актуални цени и отстъпки, моля, посетете ", + "invite": "Покани", + "inviteRegenerate": "Регениране на покана", + "inviteRegenerateDescription": "Отменете предишната покана и създайте нова", + "inviteRemove": "Премахване на покана", + "inviteRemoveError": "Неуспешно премахване на покана", + "inviteRemoveErrorDescription": "Възникна грешка при премахване на поканата.", + "inviteRemoved": "Поканата е премахната", + "inviteRemovedDescription": "Поканата за {имейл} е премахната.", + "inviteQuestionRemove": "Сигурни ли сте, че искате да премахнете поканата {email}?", + "inviteMessageRemove": "След като бъде премахната, тази покана няма да е валидна. Винаги можете да поканите потребителя отново по-късно.", + "inviteMessageConfirm": "За да потвърдите, въведете имейл адреса на поканата по-долу.", + "inviteQuestionRegenerate": "Сигурни ли сте, че искате да регенерирате поканата за {email}? Това ще отмени предишната покана.", + "inviteRemoveConfirm": "Потвърждение на премахването на поканата", + "inviteRegenerated": "Поканата е регенерирана", + "inviteSent": "Нова покана е изпратена на {email}.", + "inviteSentEmail": "Изпращане на имейл известие до потребителя", + "inviteGenerate": "Нова покана е генерирана за {email}.", + "inviteDuplicateError": "Дублиране на покана", + "inviteDuplicateErrorDescription": "Покана за този потребител вече съществува.", + "inviteRateLimitError": "Лимитът на регенерации е надвишен", + "inviteRateLimitErrorDescription": "Надвишили сте лимита от 3 регенерации на час. Моля, опитайте отново по-късно.", + "inviteRegenerateError": "Неуспешно регениране на поканата", + "inviteRegenerateErrorDescription": "Възникна грешка при регенирането на поканата.", + "inviteValidityPeriod": "Период на валидност", + "inviteValidityPeriodSelect": "Изберете период на валидност", + "inviteRegenerateMessage": "Поканата е регенерирана. Потребителят трябва да достъпи линка по-долу, за да приеме поканата.", + "inviteRegenerateButton": "Регениране", + "expiresAt": "Изтича на", + "accessRoleUnknown": "Непозната роля", + "placeholder": "Запълване", + "userErrorOrgRemove": "Неуспешно премахване на потребител", + "userErrorOrgRemoveDescription": "Възникна грешка при премахване на потребителя.", + "userOrgRemoved": "Потребителят е премахнат", + "userOrgRemovedDescription": "Потребителят {email} беше премахнат от организацията.", + "userQuestionOrgRemove": "Сигурни ли сте, че искате да премахнете {email} от организацията?", + "userMessageOrgRemove": "След като бъде премахнат, този потребител няма да има достъп до организацията. Винаги можете да го поканите отново по-късно, но той ще трябва да приеме отново поканата.", + "userMessageOrgConfirm": "За да потвърдите, въведете името на потребителя по-долу.", + "userRemoveOrgConfirm": "Потвърдете премахването на потребителя", + "userRemoveOrg": "Премахване на потребителя от организацията", + "users": "Потребители", + "accessRoleMember": "Член", + "accessRoleOwner": "Собственик", + "userConfirmed": "Потвърдено", + "idpNameInternal": "Вътрешен", + "emailInvalid": "Невалиден имейл адрес", + "inviteValidityDuration": "Моля, изберете продължителност", + "accessRoleSelectPlease": "Моля, изберете роля", + "usernameRequired": "Необходимо е потребителско име", + "idpSelectPlease": "Моля, изберете доставчик на идентичност", + "idpGenericOidc": "Основен OAuth2/OIDC доставчик.", + "accessRoleErrorFetch": "Неуспешно извличане на роли", + "accessRoleErrorFetchDescription": "Възникна грешка при извличане на ролите", + "idpErrorFetch": "Неуспешно извличане на доставчици на идентичност", + "idpErrorFetchDescription": "Възникна грешка при извличане на доставчиците на идентичност", + "userErrorExists": "Потребителят вече съществува", + "userErrorExistsDescription": "Този потребител вече е член на организацията.", + "inviteError": "Неуспешно поканване на потребител", + "inviteErrorDescription": "Възникна грешка при поканването на потребителя", + "userInvited": "Потребителят е поканен", + "userInvitedDescription": "Потребителят беше успешно поканен.", + "userErrorCreate": "Неуспешно създаване на потребител", + "userErrorCreateDescription": "Възникна грешка при създаване на потребителя", + "userCreated": "Потребителят е създаден", + "userCreatedDescription": "Потребителят беше успешно създаден.", + "userTypeInternal": "Вътрешен потребител", + "userTypeInternalDescription": "Поканете потребител да се присъедини директно към вашата организация.", + "userTypeExternal": "Външен потребител", + "userTypeExternalDescription": "Създайте потребител с външен доставчик на идентичност.", + "accessUserCreateDescription": "Следвайте стъпките по-долу, за да създадете нов потребител", + "userSeeAll": "Виж всички потребители", + "userTypeTitle": "Тип потребител", + "userTypeDescription": "Определете как искате да създадете потребителя", + "userSettings": "Информация за потребителя", + "userSettingsDescription": "Въведете данните за новия потребител", + "inviteEmailSent": "Изпратете покана по имейл до потребителя", + "inviteValid": "Валидна за", + "selectDuration": "Изберете продължителност", + "accessRoleSelect": "Изберете роля", + "inviteEmailSentDescription": "Имейлът е изпратен до потребителя с достъпния линк по-долу. Те трябва да достъпят линка, за да приемат поканата.", + "inviteSentDescription": "Потребителят е поканен. Те трябва да достъпят линка по-долу, за да приемат поканата.", + "inviteExpiresIn": "Поканата ще изтече след {days, plural, one {# ден} other {# дни}}.", + "idpTitle": "Доставчик на идентичност", + "idpSelect": "Изберете доставчика на идентичност за външния потребител", + "idpNotConfigured": "Няма конфигурирани доставчици на идентичност. Моля, конфигурирайте доставчик на идентичност, преди да създавате външни потребители.", + "usernameUniq": "Това трябва да съответства на уникалното потребителско име, което съществува във избрания доставчик на идентичност.", + "emailOptional": "Имейл (по избор)", + "nameOptional": "Име (по избор)", + "accessControls": "Контрол на достъпа", + "userDescription2": "Управление на настройките на този потребител", + "accessRoleErrorAdd": "Неуспешно добавяне на потребител към роля", + "accessRoleErrorAddDescription": "Възникна грешка при добавяне на потребителя към ролята.", + "userSaved": "Потребителят е запазен", + "userSavedDescription": "Потребителят беше актуализиран.", + "autoProvisioned": "Автоматично предоставено", + "autoProvisionedDescription": "Позволете този потребител да бъде автоматично управляван от доставчик на идентификационни данни", + "accessControlsDescription": "Управлявайте какво може да достъпва и прави този потребител в организацията", + "accessControlsSubmit": "Запазване на контролите за достъп", + "roles": "Роли", + "accessUsersRoles": "Управление на потребители и роли", + "accessUsersRolesDescription": "Поканете потребители и ги добавете към роли, за да управлявате достъпа до вашата организация", + "key": "Ключ", + "createdAt": "Създаден на", + "proxyErrorInvalidHeader": "Невалидна стойност за заглавие на хоста. Използвайте формат на име на домейн, или оставете празно поле за да премахнете персонализирано заглавие на хост.", + "proxyErrorTls": "Невалидно име на TLS сървър. Използвайте формат на име на домейн, или оставете празно за да премахнете името на TLS сървъра.", + "proxyEnableSSL": "Активиране на SSL (https)", + "targetErrorFetch": "Неуспешно извличане на цели", + "targetErrorFetchDescription": "Възникна грешка при извличане на целите", + "siteErrorFetch": "Неуспешно извличане на ресурс", + "siteErrorFetchDescription": "Възникна грешка при извличане на ресурса", + "targetErrorDuplicate": "Дубликат на цел", + "targetErrorDuplicateDescription": "Цел с тези настройки вече съществува", + "targetWireGuardErrorInvalidIp": "Невалиден таргет IP", + "targetWireGuardErrorInvalidIpDescription": "Таргет IP трябва да бъде в рамките на подмрежата на сайта", + "targetsUpdated": "Целите са актуализирани", + "targetsUpdatedDescription": "Целите и настройките бяха успешно актуализирани", + "targetsErrorUpdate": "Неуспешно актуализиране на целите", + "targetsErrorUpdateDescription": "Възникна грешка при актуализиране на целите", + "targetTlsUpdate": "Настройките на TLS са актуализирани", + "targetTlsUpdateDescription": "Вашите настройки на TLS бяха успешно актуализирани", + "targetErrorTlsUpdate": "Неуспешно актуализиране на настройки на TLS", + "targetErrorTlsUpdateDescription": "Възникна грешка при актуализиране на настройки на TLS", + "proxyUpdated": "Настройките на прокси са актуализирани", + "proxyUpdatedDescription": "Вашите настройки на прокси бяха успешно актуализирани", + "proxyErrorUpdate": "Неуспешно актуализиране на настройки на прокси", + "proxyErrorUpdateDescription": "Възникна грешка при актуализиране на настройки на прокси", + "targetAddr": "IP / Хост име", + "targetPort": "Порт", + "targetProtocol": "Протокол", + "targetTlsSettings": "Конфигурация на защитена връзка", + "targetTlsSettingsDescription": "Конфигурирайте SSL/TLS настройките за вашия ресурс", + "targetTlsSettingsAdvanced": "Разширени TLS настройки", + "targetTlsSni": "Име на TLS сървър (SNI)", + "targetTlsSniDescription": "Името на TLS сървъра за използване за SNI. Оставете празно, за да използвате подразбиране.", + "targetTlsSubmit": "Запазване на настройките", + "targets": "Конфигурация на целите", + "targetsDescription": "Настройте цели за маршрутиране на трафик към вашите бекенд услуги", + "targetStickySessions": "Активиране на постоянни сесии", + "targetStickySessionsDescription": "Запазване на връзките със същото задно целево място за цялата сесия.", + "methodSelect": "Изберете метод", + "targetSubmit": "Добавяне на цел", + "targetNoOne": "Няма цели. Добавете цел чрез формата.", + "targetNoOneDescription": "Добавянето на повече от една цел ще активира натоварването на баланса.", + "targetsSubmit": "Запазване на целите", + "proxyAdditional": "Допълнителни настройки на прокси", + "proxyAdditionalDescription": "Конфигурирайте как вашият ресурс обработва прокси настройки", + "proxyCustomHeader": "Персонализиран хост заглавие", + "proxyCustomHeaderDescription": "Хост заглавието, което да зададете при прокси заявките. Оставете празно, за да използвате подразбиране.", + "proxyAdditionalSubmit": "Запазване на прокси настройките", + "subnetMaskErrorInvalid": "Невалидна маска на мрежа. Трябва да е между 0 и 32.", + "ipAddressErrorInvalidFormat": "Невалиден формат на IP адрес", + "ipAddressErrorInvalidOctet": "Невалиден октет на IP адрес", + "path": "Път", + "matchPath": "Път на съвпадение", + "ipAddressRange": "IP обхват", + "rulesErrorFetch": "Неуспешно извличане на правила", + "rulesErrorFetchDescription": "Възникна грешка при извличане на правилата", + "rulesErrorDuplicate": "Дубликат на правило", + "rulesErrorDuplicateDescription": "Правило с тези настройки вече съществува", + "rulesErrorInvalidIpAddressRange": "Невалиден CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "Моля, въведете валидна стойност на CIDR", + "rulesErrorInvalidUrl": "Невалиден URL път", + "rulesErrorInvalidUrlDescription": "Моля, въведете валидна стойност за URL път", + "rulesErrorInvalidIpAddress": "Невалиден IP", + "rulesErrorInvalidIpAddressDescription": "Моля, въведете валиден IP адрес", + "rulesErrorUpdate": "Неуспешно актуализиране на правилата", + "rulesErrorUpdateDescription": "Възникна грешка при актуализиране на правилата", + "rulesUpdated": "Активиране на правилата", + "rulesUpdatedDescription": "Оценяването на правилата беше актуализирано", + "rulesMatchIpAddressRangeDescription": "Въведете адрес във формат CIDR (напр. 103.21.244.0/22)", + "rulesMatchIpAddress": "Въведете IP адрес (напр. 103.21.244.12)", + "rulesMatchUrl": "Въведете URL път или модел (напр. /api/v1/todos или /api/v1/*)", + "rulesErrorInvalidPriority": "Невалиден приоритет", + "rulesErrorInvalidPriorityDescription": "Моля, въведете валиден приоритет", + "rulesErrorDuplicatePriority": "Дублирани приоритети", + "rulesErrorDuplicatePriorityDescription": "Моля, въведете уникални приоритети", + "ruleUpdated": "Правилата са актуализирани", + "ruleUpdatedDescription": "Правилата бяха успешно актуализирани", + "ruleErrorUpdate": "Операцията не бе успешна", + "ruleErrorUpdateDescription": "Възникна грешка по време на операцията за запис", + "rulesPriority": "Приоритет", + "rulesAction": "Действие", + "rulesMatchType": "Тип на съвпадение", + "value": "Стойност", + "rulesAbout": "Относно правилата", + "rulesAboutDescription": "Правилата ви позволяват да контролирате достъпа до вашия ресурс въз основа на набор от критерии. Можете да създавате правила за разрешаване или отказ на достъп въз основа на IP адрес или URL път.", + "rulesActions": "Действия", + "rulesActionAlwaysAllow": "Винаги позволи: заобикаля всички методи за автентикация", + "rulesActionAlwaysDeny": "Винаги отказвай: блокиране на всички заявки; не може да се направи опит за автентикация", + "rulesActionPassToAuth": "Прехвърляне към удостоверяване: Позволяване опити за методи на удостоверяване", + "rulesMatchCriteria": "Критерии за съответствие", + "rulesMatchCriteriaIpAddress": "Съответствие с конкретен IP адрес", + "rulesMatchCriteriaIpAddressRange": "Съответства на диапазон от IP адреси в CIDR нотация", + "rulesMatchCriteriaUrl": "Съответствие с път или шаблон URL", + "rulesEnable": "Активирай правилата", + "rulesEnableDescription": "Активиране или деактивиране на оценката на правилата за този ресурс", + "rulesResource": "Конфигурация на правилата за ресурси", + "rulesResourceDescription": "Конфигурирайте правила, за да контролирате достъпа до вашия ресурс", + "ruleSubmit": "Добави правило", + "rulesNoOne": "Няма правила. Добавете правило чрез формуляра.", + "rulesOrder": "Правилата се оценяват по приоритет в нарастващ ред.", + "rulesSubmit": "Запазване на правилата", + "resourceErrorCreate": "Грешка при създаване на ресурс", + "resourceErrorCreateDescription": "Възникна грешка при създаването на ресурса", + "resourceErrorCreateMessage": "Грешка при създаване на ресурс:", + "resourceErrorCreateMessageDescription": "Възникна неочаквана грешка", + "sitesErrorFetch": "Грешка при получаване на сайтове", + "sitesErrorFetchDescription": "Възникна грешка при получаването на сайтовете", + "domainsErrorFetch": "Грешка при получаването на домейни", + "domainsErrorFetchDescription": "Възникна грешка при получаването на домейните", + "none": "Няма", + "unknown": "Неизвестно", + "resources": "Ресурси", + "resourcesDescription": "Ресурсите са проксита за приложения, работещи във вашата частна мрежа. Създайте ресурс за всеки HTTP/HTTPS или суров TCP/UDP услуга във вашата частна мрежа. Всеки ресурс трябва да бъде свързан със сайт, за да се осигури частна, сигурна свързаност чрез криптиран WireGuard тунел.", + "resourcesWireGuardConnect": "Сигурно свързване с криптиране на WireGuard", + "resourcesMultipleAuthenticationMethods": "Конфигуриране на множество методи за автентикация", + "resourcesUsersRolesAccess": "Контрол на достъпа, базиран на потребители и роли", + "resourcesErrorUpdate": "Неуспешно превключване на ресурса", + "resourcesErrorUpdateDescription": "Възникна грешка при актуализиране на ресурса", + "access": "Достъп", + "shareLink": "{resource} Сподели връзка", + "resourceSelect": "Изберете ресурс", + "shareLinks": "Споделени връзки", + "share": "Споделени връзки", + "shareDescription2": "Създайте споделени връзки към вашите ресурси. Връзките осигуряват временно или неограничено достъп до вашия ресурс. Можете да конфигурирате продължителността на изтичане на връзката при създаването й.", + "shareEasyCreate": "Лесно за създаване и споделяне", + "shareConfigurableExpirationDuration": "Конфигурируемо време на изтичане", + "shareSecureAndRevocable": "Сигурни и отменяеми", + "nameMin": "Името трябва да съдържа поне {len} знака.", + "nameMax": "Името не трябва да е по-дълго от {len} знака.", + "sitesConfirmCopy": "Моля, потвърдете, че сте копирали конфигурацията.", + "unknownCommand": "Неизвестна команда", + "newtErrorFetchReleases": "Неуспешно получаване на информация за изданието: {err}", + "newtErrorFetchLatest": "Грешка при получаването на последното издание: {err}", + "newtEndpoint": "Newt Изходен пункт", "newtId": "Newt ID", "newtSecretKey": "Newt Secret Key", - "architecture": "Architecture", - "sites": "Sites", - "siteWgAnyClients": "Use any WireGuard client to connect. You will have to address your internal resources using the peer IP.", - "siteWgCompatibleAllClients": "Compatible with all WireGuard clients", - "siteWgManualConfigurationRequired": "Manual configuration required", - "userErrorNotAdminOrOwner": "User is not an admin or owner", - "pangolinSettings": "Settings - Pangolin", - "accessRoleYour": "Your role:", - "accessRoleSelect2": "Select a role", - "accessUserSelect": "Select a user", - "otpEmailEnter": "Enter an email", - "otpEmailEnterDescription": "Press enter to add an email after typing it in the input field.", - "otpEmailErrorInvalid": "Invalid email address. Wildcard (*) must be the entire local part.", - "otpEmailSmtpRequired": "SMTP Required", - "otpEmailSmtpRequiredDescription": "SMTP must be enabled on the server to use one-time password authentication.", - "otpEmailTitle": "One-time Passwords", - "otpEmailTitleDescription": "Require email-based authentication for resource access", - "otpEmailWhitelist": "Email Whitelist", - "otpEmailWhitelistList": "Whitelisted Emails", - "otpEmailWhitelistListDescription": "Only users with these email addresses will be able to access this resource. They will be prompted to enter a one-time password sent to their email. Wildcards (*@example.com) can be used to allow any email address from a domain.", - "otpEmailWhitelistSave": "Save Whitelist", - "passwordAdd": "Add Password", - "passwordRemove": "Remove Password", - "pincodeAdd": "Add PIN Code", - "pincodeRemove": "Remove PIN Code", - "resourceAuthMethods": "Authentication Methods", - "resourceAuthMethodsDescriptions": "Allow access to the resource via additional auth methods", - "resourceAuthSettingsSave": "Saved successfully", - "resourceAuthSettingsSaveDescription": "Authentication settings have been saved", - "resourceErrorAuthFetch": "Failed to fetch data", - "resourceErrorAuthFetchDescription": "An error occurred while fetching the data", - "resourceErrorPasswordRemove": "Error removing resource password", - "resourceErrorPasswordRemoveDescription": "An error occurred while removing the resource password", - "resourceErrorPasswordSetup": "Error setting resource password", - "resourceErrorPasswordSetupDescription": "An error occurred while setting the resource password", - "resourceErrorPincodeRemove": "Error removing resource pincode", - "resourceErrorPincodeRemoveDescription": "An error occurred while removing the resource pincode", - "resourceErrorPincodeSetup": "Error setting resource PIN code", - "resourceErrorPincodeSetupDescription": "An error occurred while setting the resource PIN code", - "resourceErrorUsersRolesSave": "Failed to set roles", - "resourceErrorUsersRolesSaveDescription": "An error occurred while setting the roles", - "resourceErrorWhitelistSave": "Failed to save whitelist", - "resourceErrorWhitelistSaveDescription": "An error occurred while saving the whitelist", - "resourcePasswordSubmit": "Enable Password Protection", - "resourcePasswordProtection": "Password Protection {status}", - "resourcePasswordRemove": "Resource password removed", - "resourcePasswordRemoveDescription": "The resource password has been removed successfully", - "resourcePasswordSetup": "Resource password set", - "resourcePasswordSetupDescription": "The resource password has been set successfully", - "resourcePasswordSetupTitle": "Set Password", - "resourcePasswordSetupTitleDescription": "Set a password to protect this resource", - "resourcePincode": "PIN Code", - "resourcePincodeSubmit": "Enable PIN Code Protection", - "resourcePincodeProtection": "PIN Code Protection {status}", - "resourcePincodeRemove": "Resource pincode removed", - "resourcePincodeRemoveDescription": "The resource password has been removed successfully", - "resourcePincodeSetup": "Resource PIN code set", - "resourcePincodeSetupDescription": "The resource pincode has been set successfully", - "resourcePincodeSetupTitle": "Set Pincode", - "resourcePincodeSetupTitleDescription": "Set a pincode to protect this resource", - "resourceRoleDescription": "Admins can always access this resource.", - "resourceUsersRoles": "Users & Roles", - "resourceUsersRolesDescription": "Configure which users and roles can visit this resource", - "resourceUsersRolesSubmit": "Save Users & Roles", - "resourceWhitelistSave": "Saved successfully", - "resourceWhitelistSaveDescription": "Whitelist settings have been saved", - "ssoUse": "Use Platform SSO", - "ssoUseDescription": "Existing users will only have to log in once for all resources that have this enabled.", - "proxyErrorInvalidPort": "Invalid port number", - "subdomainErrorInvalid": "Invalid subdomain", - "domainErrorFetch": "Error fetching domains", - "domainErrorFetchDescription": "An error occurred when fetching the domains", - "resourceErrorUpdate": "Failed to update resource", - "resourceErrorUpdateDescription": "An error occurred while updating the resource", - "resourceUpdated": "Resource updated", - "resourceUpdatedDescription": "The resource has been updated successfully", - "resourceErrorTransfer": "Failed to transfer resource", - "resourceErrorTransferDescription": "An error occurred while transferring the resource", - "resourceTransferred": "Resource transferred", - "resourceTransferredDescription": "The resource has been transferred successfully", - "resourceErrorToggle": "Failed to toggle resource", - "resourceErrorToggleDescription": "An error occurred while updating the resource", - "resourceVisibilityTitle": "Visibility", - "resourceVisibilityTitleDescription": "Completely enable or disable resource visibility", - "resourceGeneral": "General Settings", - "resourceGeneralDescription": "Configure the general settings for this resource", - "resourceEnable": "Enable Resource", - "resourceTransfer": "Transfer Resource", - "resourceTransferDescription": "Transfer this resource to a different site", - "resourceTransferSubmit": "Transfer Resource", - "siteDestination": "Destination Site", - "searchSites": "Search sites", - "accessRoleCreate": "Create Role", - "accessRoleCreateDescription": "Create a new role to group users and manage their permissions.", - "accessRoleCreateSubmit": "Create Role", - "accessRoleCreated": "Role created", - "accessRoleCreatedDescription": "The role has been successfully created.", - "accessRoleErrorCreate": "Failed to create role", - "accessRoleErrorCreateDescription": "An error occurred while creating the role.", - "accessRoleErrorNewRequired": "New role is required", - "accessRoleErrorRemove": "Failed to remove role", - "accessRoleErrorRemoveDescription": "An error occurred while removing the role.", - "accessRoleName": "Role Name", - "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", - "accessRoleRemove": "Remove Role", - "accessRoleRemoveDescription": "Remove a role from the organization", - "accessRoleRemoveSubmit": "Remove Role", - "accessRoleRemoved": "Role removed", - "accessRoleRemovedDescription": "The role has been successfully removed.", - "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", - "manage": "Manage", - "sitesNotFound": "No sites found.", - "pangolinServerAdmin": "Server Admin - Pangolin", - "licenseTierProfessional": "Professional License", - "licenseTierEnterprise": "Enterprise License", - "licenseTierCommercial": "Commercial License", - "licensed": "Licensed", - "yes": "Yes", - "no": "No", - "sitesAdditional": "Additional Sites", - "licenseKeys": "License Keys", - "sitestCountDecrease": "Decrease site count", - "sitestCountIncrease": "Increase site count", - "idpManage": "Manage Identity Providers", - "idpManageDescription": "View and manage identity providers in the system", - "idpDeletedDescription": "Identity provider deleted successfully", + "architecture": "Архитектура", + "sites": "Сайтове", + "siteWgAnyClients": "Използвайте всеки WireGuard клиент за свързване. Ще трябва да адресирате вашите вътрешни ресурси, използвайки IP на равностойния.", + "siteWgCompatibleAllClients": "Съвместим с всички WireGuard клиенти", + "siteWgManualConfigurationRequired": "Необходима е ръчна конфигурация", + "userErrorNotAdminOrOwner": "Потребителят не е администратор или собственик", + "pangolinSettings": "Настройки - Панголин", + "accessRoleYour": "Вашата роля:", + "accessRoleSelect2": "Изберете роля", + "accessUserSelect": "Изберете потребител", + "otpEmailEnter": "Въведете имейл", + "otpEmailEnterDescription": "Натиснете Enter, за да добавите имейл след като сте го въведели в полето за въвеждане.", + "otpEmailErrorInvalid": "Невалиден имейл адрес. Wilcard (*) трябва да е цялата част от локалния адрес.", + "otpEmailSmtpRequired": "Необходимо е SMTP", + "otpEmailSmtpRequiredDescription": "SMTP трябва да бъде активиран на сървъра, за да използвате еднократни пароли за автентикация.", + "otpEmailTitle": "Еднократни пароли", + "otpEmailTitleDescription": "Изисквайте автентикация базирана на имейл за достъп до ресурси", + "otpEmailWhitelist": "Бял списък на имейли", + "otpEmailWhitelistList": "Имейли в белия списък", + "otpEmailWhitelistListDescription": "Само потребители с тези имейл адреси ще могат да имат достъп до този ресурс. Те ще бъдат помолени да въведат еднократна парола, изпратена на техния имейл. Може да се използват wildcards (*@example.com), за да се позволи на всеки имейл адрес от домейн.", + "otpEmailWhitelistSave": "Запазване на белия списък", + "passwordAdd": "Добави парола", + "passwordRemove": "Премахни парола", + "pincodeAdd": "Добави ПИН код", + "pincodeRemove": "Премахни ПИН код", + "resourceAuthMethods": "Методи за автентикация", + "resourceAuthMethodsDescriptions": "Позволете достъп до ресурса чрез допълнителни методи за автентикация", + "resourceAuthSettingsSave": "Запазено успешно", + "resourceAuthSettingsSaveDescription": "Настройките за автентикация са запазени успешно", + "resourceErrorAuthFetch": "Неуспешно получаване на данни", + "resourceErrorAuthFetchDescription": "Възникна грешка при получаването на данните", + "resourceErrorPasswordRemove": "Грешка при премахване на паролата за ресурс", + "resourceErrorPasswordRemoveDescription": "Възникна грешка при премахването на паролата за ресурс", + "resourceErrorPasswordSetup": "Грешка при настройване на паролата за ресурс", + "resourceErrorPasswordSetupDescription": "Възникна грешка при настройването на паролата за ресурс", + "resourceErrorPincodeRemove": "Грешка при премахване на ПИН кода за ресурс", + "resourceErrorPincodeRemoveDescription": "Възникна грешка при премахването на ПИН кода за ресурс", + "resourceErrorPincodeSetup": "Грешка при настройване на ПИН кода за ресурс", + "resourceErrorPincodeSetupDescription": "Възникна грешка при настройването на ПИН кода за ресурс", + "resourceErrorUsersRolesSave": "Неуспешно задаване на роли", + "resourceErrorUsersRolesSaveDescription": "Възникна грешка при задаването на ролите", + "resourceErrorWhitelistSave": "Неуспешно запазване на белия списък", + "resourceErrorWhitelistSaveDescription": "Възникна грешка при запазването на белия списък", + "resourcePasswordSubmit": "Активирай защита с парола", + "resourcePasswordProtection": "Защита с парола {status}", + "resourcePasswordRemove": "Паролата на ресурса е премахната", + "resourcePasswordRemoveDescription": "Премахването на паролата за ресурс беше успешно", + "resourcePasswordSetup": "Паролата за ресурс е настроена", + "resourcePasswordSetupDescription": "Паролата за ресурс беше успешно настроена", + "resourcePasswordSetupTitle": "Задай парола", + "resourcePasswordSetupTitleDescription": "Задайте парола, за да защитите този ресурс", + "resourcePincode": "ПИН код", + "resourcePincodeSubmit": "Активирай защита с ПИН код", + "resourcePincodeProtection": "Защита с ПИН код {status}", + "resourcePincodeRemove": "Премахнат ПИН код за ресурс", + "resourcePincodeRemoveDescription": "Премахването на ПИН кода за ресурс беше успешно", + "resourcePincodeSetup": "Настроен ПИН код за ресурс", + "resourcePincodeSetupDescription": "Настройването на ПИН кода за ресурс беше успешно", + "resourcePincodeSetupTitle": "Задай ПИН код", + "resourcePincodeSetupTitleDescription": "Задайте ПИН код, за да защитите този ресурс", + "resourceRoleDescription": "Администраторите винаги могат да имат достъп до този ресурс.", + "resourceUsersRoles": "Потребители и роли", + "resourceUsersRolesDescription": "Конфигурирайте кои потребители и роли могат да посещават този ресурс", + "resourceUsersRolesSubmit": "Запазете потребители и роли", + "resourceWhitelistSave": "Успешно запазено", + "resourceWhitelistSaveDescription": "Настройките на белия списък са запазени", + "ssoUse": "Използвай платформен SSO", + "ssoUseDescription": "Съществуващите потребители ще трябва да влязат само веднъж за всички ресурси, които имат тази опция активирана.", + "proxyErrorInvalidPort": "Невалиден номер на порт", + "subdomainErrorInvalid": "Невалиден поддомейн", + "domainErrorFetch": "Грешка при получаването на домейни", + "domainErrorFetchDescription": "Възникна грешка при получаването на домейните", + "resourceErrorUpdate": "Неуспешно актуализиране на ресурса", + "resourceErrorUpdateDescription": "Възникна грешка при актуализирането на ресурса", + "resourceUpdated": "Ресурсът е обновен", + "resourceUpdatedDescription": "Ресурсът беше успешно обновен", + "resourceErrorTransfer": "Неуспешно прехвърляне на ресурса", + "resourceErrorTransferDescription": "Възникна грешка при прехвърлянето на ресурса", + "resourceTransferred": "Ресурсът е прехвърлен", + "resourceTransferredDescription": "Ресурсът беше успешно прехвърлен", + "resourceErrorToggle": "Неуспешно превключване на ресурса", + "resourceErrorToggleDescription": "Възникна грешка при актуализирането на ресурса", + "resourceVisibilityTitle": "Видимост", + "resourceVisibilityTitleDescription": "Напълно активирайте или деактивирайте видимостта на ресурса", + "resourceGeneral": "Общи настройки", + "resourceGeneralDescription": "Конфигурирайте общите настройки за този ресурс", + "resourceEnable": "Активирайте ресурс", + "resourceTransfer": "Прехвърлете ресурс", + "resourceTransferDescription": "Прехвърлете този ресурс към различен сайт", + "resourceTransferSubmit": "Прехвърлете ресурс", + "siteDestination": "Дестинационен сайт", + "searchSites": "Търси сайтове", + "accessRoleCreate": "Създайте роля", + "accessRoleCreateDescription": "Създайте нова роля за групиране на потребители и управление на техните разрешения.", + "accessRoleCreateSubmit": "Създайте роля", + "accessRoleCreated": "Ролята е създадена", + "accessRoleCreatedDescription": "Ролята беше успешно създадена.", + "accessRoleErrorCreate": "Неуспешно създаване на роля", + "accessRoleErrorCreateDescription": "Възникна грешка при създаването на ролята.", + "accessRoleErrorNewRequired": "Нова роля е необходима", + "accessRoleErrorRemove": "Неуспешно премахване на роля", + "accessRoleErrorRemoveDescription": "Възникна грешка при премахването на роля.", + "accessRoleName": "Име на роля", + "accessRoleQuestionRemove": "Ще изтриете ролята {name}. Не можете да отмените това действие.", + "accessRoleRemove": "Премахни роля", + "accessRoleRemoveDescription": "Премахни роля от организацията", + "accessRoleRemoveSubmit": "Премахни роля", + "accessRoleRemoved": "Ролята е премахната", + "accessRoleRemovedDescription": "Ролята беше успешно премахната.", + "accessRoleRequiredRemove": "Преди да изтриете тази роля, моля изберете нова роля, към която да прехвърлите настоящите членове.", + "manage": "Управление", + "sitesNotFound": "Няма намерени сайтове.", + "pangolinServerAdmin": "Администратор на сървър - Панголин", + "licenseTierProfessional": "Професионален лиценз", + "licenseTierEnterprise": "Предприятие лиценз", + "licenseTierCommercial": "Търговски лиценз", + "licensed": "Лицензиран", + "yes": "Да", + "no": "Не", + "sitesAdditional": "Допълнителни сайтове", + "licenseKeys": "Лицензионни ключове", + "sitestCountDecrease": "Намаляване на броя на сайтовете", + "sitestCountIncrease": "Увеличаване на броя на сайтовете", + "idpManage": "Управление на доставчици на идентичност", + "idpManageDescription": "Прегледайте и управлявайте доставчици на идентичност в системата", + "idpDeletedDescription": "Доставчик на идентичност успешно изтрит", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", - "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", - "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", - "idpConfirmDelete": "Confirm Delete Identity Provider", - "idpDelete": "Delete Identity Provider", - "idp": "Identity Providers", - "idpSearch": "Search identity providers...", - "idpAdd": "Add Identity Provider", - "idpClientIdRequired": "Client ID is required.", - "idpClientSecretRequired": "Client Secret is required.", - "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", - "idpErrorTokenUrlInvalid": "Token URL must be a valid URL.", - "idpPathRequired": "Identifier Path is required.", - "idpScopeRequired": "Scopes are required.", - "idpOidcDescription": "Configure an OpenID Connect identity provider", - "idpCreatedDescription": "Identity provider created successfully", - "idpCreate": "Create Identity Provider", - "idpCreateDescription": "Configure a new identity provider for user authentication", - "idpSeeAll": "See All Identity Providers", - "idpSettingsDescription": "Configure the basic information for your identity provider", - "idpDisplayName": "A display name for this identity provider", - "idpAutoProvisionUsers": "Auto Provision Users", - "idpAutoProvisionUsersDescription": "When enabled, users will be automatically created in the system upon first login with the ability to map users to roles and organizations.", - "licenseBadge": "Professional", - "idpType": "Provider Type", - "idpTypeDescription": "Select the type of identity provider you want to configure", - "idpOidcConfigure": "OAuth2/OIDC Configuration", - "idpOidcConfigureDescription": "Configure the OAuth2/OIDC provider endpoints and credentials", - "idpClientId": "Client ID", - "idpClientIdDescription": "The OAuth2 client ID from your identity provider", - "idpClientSecret": "Client Secret", - "idpClientSecretDescription": "The OAuth2 client secret from your identity provider", - "idpAuthUrl": "Authorization URL", - "idpAuthUrlDescription": "The OAuth2 authorization endpoint URL", - "idpTokenUrl": "Token URL", - "idpTokenUrlDescription": "The OAuth2 token endpoint URL", - "idpOidcConfigureAlert": "Important Information", - "idpOidcConfigureAlertDescription": "After creating the identity provider, you will need to configure the callback URL in your identity provider's settings. The callback URL will be provided after successful creation.", - "idpToken": "Token Configuration", - "idpTokenDescription": "Configure how to extract user information from the ID token", - "idpJmespathAbout": "About JMESPath", - "idpJmespathAboutDescription": "The paths below use JMESPath syntax to extract values from the ID token.", - "idpJmespathAboutDescriptionLink": "Learn more about JMESPath", - "idpJmespathLabel": "Identifier Path", - "idpJmespathLabelDescription": "The path to the user identifier in the ID token", - "idpJmespathEmailPathOptional": "Email Path (Optional)", - "idpJmespathEmailPathOptionalDescription": "The path to the user's email in the ID token", - "idpJmespathNamePathOptional": "Name Path (Optional)", - "idpJmespathNamePathOptionalDescription": "The path to the user's name in the ID token", - "idpOidcConfigureScopes": "Scopes", - "idpOidcConfigureScopesDescription": "Space-separated list of OAuth2 scopes to request", - "idpSubmit": "Create Identity Provider", - "orgPolicies": "Organization Policies", - "idpSettings": "{idpName} Settings", - "idpCreateSettingsDescription": "Configure the settings for your identity provider", - "roleMapping": "Role Mapping", - "orgMapping": "Organization Mapping", - "orgPoliciesSearch": "Search organization policies...", - "orgPoliciesAdd": "Add Organization Policy", - "orgRequired": "Organization is required", - "error": "Error", - "success": "Success", - "orgPolicyAddedDescription": "Policy added successfully", - "orgPolicyUpdatedDescription": "Policy updated successfully", - "orgPolicyDeletedDescription": "Policy deleted successfully", - "defaultMappingsUpdatedDescription": "Default mappings updated successfully", - "orgPoliciesAbout": "About Organization Policies", - "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token.", - "orgPoliciesAboutDescriptionLink": "See documentation, for more information.", - "defaultMappingsOptional": "Default Mappings (Optional)", - "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", - "defaultMappingsRole": "Default Role Mapping", - "defaultMappingsRoleDescription": "The result of this expression must return the role name as defined in the organization as a string.", - "defaultMappingsOrg": "Default Organization Mapping", - "defaultMappingsOrgDescription": "This expression must return the org ID or true for the user to be allowed to access the organization.", - "defaultMappingsSubmit": "Save Default Mappings", - "orgPoliciesEdit": "Edit Organization Policy", - "org": "Organization", - "orgSelect": "Select organization", - "orgSearch": "Search org", - "orgNotFound": "No org found.", - "roleMappingPathOptional": "Role Mapping Path (Optional)", - "orgMappingPathOptional": "Organization Mapping Path (Optional)", - "orgPolicyUpdate": "Update Policy", - "orgPolicyAdd": "Add Policy", - "orgPolicyConfig": "Configure access for an organization", - "idpUpdatedDescription": "Identity provider updated successfully", - "redirectUrl": "Redirect URL", - "redirectUrlAbout": "About Redirect URL", - "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", - "pangolinAuth": "Auth - Pangolin", - "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", - "errorOccurred": "An error occurred", - "emailErrorVerify": "Failed to verify email:", - "emailVerified": "Email successfully verified! Redirecting you...", - "verificationCodeErrorResend": "Failed to resend verification code:", - "verificationCodeResend": "Verification code resent", - "verificationCodeResendDescription": "We've resent a verification code to your email address. Please check your inbox.", - "emailVerify": "Verify Email", - "emailVerifyDescription": "Enter the verification code sent to your email address.", - "verificationCode": "Verification Code", - "verificationCodeEmailSent": "We sent a verification code to your email address.", - "submit": "Submit", - "emailVerifyResendProgress": "Resending...", - "emailVerifyResend": "Didn't receive a code? Click here to resend", - "passwordNotMatch": "Passwords do not match", + "idpQuestionRemove": "Сигурни ли сте, че искате да изтриете трайно доставчика на идентичност {name}", + "idpMessageRemove": "Това ще премахне доставчика на идентичност и всички свързани конфигурации. Потребителите, които се удостоверяват през този доставчик, вече няма да могат да влязат.", + "idpMessageConfirm": "За потвърждение, моля въведете името на доставчика на идентичност по-долу.", + "idpConfirmDelete": "Потвърдите изтриването на доставчик на идентичност", + "idpDelete": "Изтрий доставчик на идентичност", + "idp": "Доставчици на идентичност", + "idpSearch": "Търси доставчици на идентичност...", + "idpAdd": "Добавяне на доставчик на идентичност", + "idpClientIdRequired": "Необходим е идентификационен номер на клиента.", + "idpClientSecretRequired": "Необходим секретен код на клиента.", + "idpErrorAuthUrlInvalid": "URL за удостоверяване трябва да бъде валиден URL.", + "idpErrorTokenUrlInvalid": "URL токен трябва да бъде валиден URL.", + "idpPathRequired": "Идентификаторът на пътя е необходим.", + "idpScopeRequired": "Областите са задължителни.", + "idpOidcDescription": "Конфигурирайте доставчик на идентичност с OpenID Connect", + "idpCreatedDescription": "Доставчикът на идентичност създаден успешно", + "idpCreate": "Създаване на доставчик на идентичност", + "idpCreateDescription": "Конфигурирайте нов доставчик на идентичност за удостоверяване на потребителя", + "idpSeeAll": "Виж всички доставчици на идентичност", + "idpSettingsDescription": "Конфигурирайте основната информация за вашия доставчик на идентичност", + "idpDisplayName": "Име за показване за този доставчик на идентичност", + "idpAutoProvisionUsers": "Автоматично потребителско създаване", + "idpAutoProvisionUsersDescription": "Когато е активирано, потребителите ще бъдат автоматично създадени в системата при първо влизане с възможност за свързване на потребителите с роли и организации.", + "licenseBadge": "Професионален", + "idpType": "Тип доставчик", + "idpTypeDescription": "Изберете типа доставчик на идентичност, който искате да конфигурирате", + "idpOidcConfigure": "Конфигурация на OAuth2/OIDC", + "idpOidcConfigureDescription": "Конфигурирайте OAuth2/OIDC доставчика на крайни точки и кредити", + "idpClientId": "ID на клиента", + "idpClientIdDescription": "OAuth2 идентификационен номер на клиента от вашия доставчик на идентичност", + "idpClientSecret": "Секретен код на клиента", + "idpClientSecretDescription": "OAuth2 секретен код на клиента от вашия доставчик на идентичност", + "idpAuthUrl": "URL за удостоверение", + "idpAuthUrlDescription": "OAuth2 крайна точка за удостоверяване URL", + "idpTokenUrl": "URL на токена", + "idpTokenUrlDescription": "OAuth2 крайна точка на токена URL", + "idpOidcConfigureAlert": "Важно информация", + "idpOidcConfigureAlertDescription": "След създаването на доставчика на идентичност ще е необходимо да конфигурирате URL за обратна връзка в настройките на вашия доставчик на идентичност. URL за обратна връзка ще бъде предоставен след успешно създаване.", + "idpToken": "Конфигуриране на токените", + "idpTokenDescription": "Конфигурирайте как да извлечете информация за потребителя от ID токена", + "idpJmespathAbout": "Относно JMESPath", + "idpJmespathAboutDescription": "Пътищата по-долу използват синтаксиса JMESPath за извличане на стойности от ID токена.", + "idpJmespathAboutDescriptionLink": "Научете повече за JMESPath", + "idpJmespathLabel": "Идентификатор на пътя", + "idpJmespathLabelDescription": "Пътят към идентификатора на потребителя в ID токена", + "idpJmespathEmailPathOptional": "Път за имейл (по избор)", + "idpJmespathEmailPathOptionalDescription": "Пътят до имейла на потребителя в ID токена", + "idpJmespathNamePathOptional": "Път (по избор) на име", + "idpJmespathNamePathOptionalDescription": "Пътят до името на потребителя в ID токена", + "idpOidcConfigureScopes": "Области", + "idpOidcConfigureScopesDescription": "Списък от разделените се с интервали OAuth2 области, които да се заявят", + "idpSubmit": "Създайте доставчик на идентичност", + "orgPolicies": "Организационни политики", + "idpSettings": "{idpName} Настройки", + "idpCreateSettingsDescription": "Конфигурирайте настройките за вашия доставчик на идентичност", + "roleMapping": "Ролева карта", + "orgMapping": "Организационна карта", + "orgPoliciesSearch": "Търсене на организационни политики...", + "orgPoliciesAdd": "Добавяне на организационна политика", + "orgRequired": "Организацията е задължителна", + "error": "Грешка", + "success": "Успех", + "orgPolicyAddedDescription": "Политиката беше добавена успешно", + "orgPolicyUpdatedDescription": "Политиката беше актуализирана успешно", + "orgPolicyDeletedDescription": "Политиката беше изтрита успешно", + "defaultMappingsUpdatedDescription": "Файловете по подразбиране бяха актуализирани успешно", + "orgPoliciesAbout": "За Организационни политики", + "orgPoliciesAboutDescription": "Организационните политики се използват за контрол на достъпа до организации въз основа на идентификационния токен на потребителя. Можете да зададете JMESPath изрази за извличане на роля и информация за организацията от идентификационния токен.", + "orgPoliciesAboutDescriptionLink": "Вижте документацията за повече информация.", + "defaultMappingsOptional": "Файлове по подразбиране (По желание)", + "defaultMappingsOptionalDescription": "Файловете по подразбиране се използват, когато няма дефинирана организационна политика за организацията. Можете да зададете роля и файлове за организацията по подразбиране, които да се използват в този случай.", + "defaultMappingsRole": "Карта на роля по подразбиране", + "defaultMappingsRoleDescription": "Резултатът от този израз трябва да върне името на ролята, както е дефинирано в организацията, като стринг.", + "defaultMappingsOrg": "Карта на организация по подразбиране", + "defaultMappingsOrgDescription": "Този израз трябва да върне ID на организацията или 'true', за да бъде разрешен достъпът на потребителя до организацията.", + "defaultMappingsSubmit": "Запазване на файловете по подразбиране", + "orgPoliciesEdit": "Редактиране на Организационна Политика", + "org": "Организация", + "orgSelect": "Изберете организация", + "orgSearch": "Търсене на организация", + "orgNotFound": "Не е намерена организация.", + "roleMappingPathOptional": "Път на ролята (По желание)", + "orgMappingPathOptional": "Път на организацията (По желание)", + "orgPolicyUpdate": "Актуализиране на Политика", + "orgPolicyAdd": "Добавяне на Политика", + "orgPolicyConfig": "Конфигуриране на достъп за организация", + "idpUpdatedDescription": "Идентификационният доставчик беше актуализиран успешно", + "redirectUrl": "URL за пренасочване", + "redirectUrlAbout": "За URL за пренасочване", + "redirectUrlAboutDescription": "Това е URL, към който потребителите ще бъдат пренасочени след удостоверяване. Трябва да конфигурирате този URL в настройките на вашия доставчик на идентификация.", + "pangolinAuth": "Authent - Pangolin", + "verificationCodeLengthRequirements": "Вашият код за удостоверяване трябва да бъде 8 символа.", + "errorOccurred": "Възникна грешка", + "emailErrorVerify": "Неуспешно удостоверяване на имейл:", + "emailVerified": "Имейлът беше успешно удостоверен! Пренасочване...", + "verificationCodeErrorResend": "Неуспешно изпращане на код за удостоверяване отново:", + "verificationCodeResend": "Кодът за удостоверяване беше изпратен отново", + "verificationCodeResendDescription": "Изпратихме код за удостоверяване на вашия имейл адрес. Моля, проверете входящата си поща.", + "emailVerify": "Потвърждаване на имейл", + "emailVerifyDescription": "Въведете кода за удостоверяване, изпратен на вашия имейл адрес.", + "verificationCode": "Код за удостоверяване", + "verificationCodeEmailSent": "Изпратихме код за удостоверяване на вашия имейл адрес.", + "submit": "Изпращане", + "emailVerifyResendProgress": "Пренасочване...", + "emailVerifyResend": "Не получихте код? Кликнете тук, за да изпратите отново", + "passwordNotMatch": "Паролите не съвпадат", "signupError": "An error occurred while signing up", - "pangolinLogoAlt": "Pangolin Logo", - "inviteAlready": "Looks like you've been invited!", - "inviteAlreadyDescription": "To accept the invite, you must log in or create an account.", - "signupQuestion": "Already have an account?", - "login": "Log in", - "resourceNotFound": "Resource Not Found", - "resourceNotFoundDescription": "The resource you're trying to access does not exist.", - "pincodeRequirementsLength": "PIN must be exactly 6 digits", - "pincodeRequirementsChars": "PIN must only contain numbers", - "passwordRequirementsLength": "Password must be at least 1 character long", - "passwordRequirementsTitle": "Password requirements:", - "passwordRequirementLength": "At least 8 characters long", - "passwordRequirementUppercase": "At least one uppercase letter", - "passwordRequirementLowercase": "At least one lowercase letter", - "passwordRequirementNumber": "At least one number", - "passwordRequirementSpecial": "At least one special character", - "passwordRequirementsMet": "✓ Password meets all requirements", - "passwordStrength": "Password strength", - "passwordStrengthWeak": "Weak", - "passwordStrengthMedium": "Medium", - "passwordStrengthStrong": "Strong", - "passwordRequirements": "Requirements:", - "passwordRequirementLengthText": "8+ characters", - "passwordRequirementUppercaseText": "Uppercase letter (A-Z)", - "passwordRequirementLowercaseText": "Lowercase letter (a-z)", - "passwordRequirementNumberText": "Number (0-9)", - "passwordRequirementSpecialText": "Special character (!@#$%...)", - "passwordsDoNotMatch": "Passwords do not match", - "otpEmailRequirementsLength": "OTP must be at least 1 character long", - "otpEmailSent": "OTP Sent", - "otpEmailSentDescription": "An OTP has been sent to your email", - "otpEmailErrorAuthenticate": "Failed to authenticate with email", - "pincodeErrorAuthenticate": "Failed to authenticate with pincode", - "passwordErrorAuthenticate": "Failed to authenticate with password", - "poweredBy": "Powered by", - "authenticationRequired": "Authentication Required", - "authenticationMethodChoose": "Choose your preferred method to access {name}", - "authenticationRequest": "You must authenticate to access {name}", - "user": "User", - "pincodeInput": "6-digit PIN Code", - "pincodeSubmit": "Log in with PIN", - "passwordSubmit": "Log In with Password", - "otpEmailDescription": "A one-time code will be sent to this email.", - "otpEmailSend": "Send One-time Code", - "otpEmail": "One-Time Password (OTP)", - "otpEmailSubmit": "Submit OTP", - "backToEmail": "Back to Email", - "noSupportKey": "Server is running without a supporter key. Consider supporting the project!", - "accessDenied": "Access Denied", - "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", - "accessTokenError": "Error checking access token", - "accessGranted": "Access Granted", - "accessUrlInvalid": "Access URL Invalid", - "accessGrantedDescription": "You have been granted access to this resource. Redirecting you...", - "accessUrlInvalidDescription": "This shared access URL is invalid. Please contact the resource owner for a new URL.", - "tokenInvalid": "Invalid token", - "pincodeInvalid": "Invalid code", - "passwordErrorRequestReset": "Failed to request reset:", - "passwordErrorReset": "Failed to reset password:", - "passwordResetSuccess": "Password reset successfully! Back to log in...", - "passwordReset": "Reset Password", - "passwordResetDescription": "Follow the steps to reset your password", - "passwordResetSent": "We'll send a password reset code to this email address.", - "passwordResetCode": "Reset Code", - "passwordResetCodeDescription": "Check your email for the reset code.", - "passwordNew": "New Password", - "passwordNewConfirm": "Confirm New Password", - "pincodeAuth": "Authenticator Code", - "pincodeSubmit2": "Submit Code", - "passwordResetSubmit": "Request Reset", - "passwordBack": "Back to Password", - "loginBack": "Go back to log in", - "signup": "Sign up", - "loginStart": "Log in to get started", - "idpOidcTokenValidating": "Validating OIDC token", - "idpOidcTokenResponse": "Validate OIDC token response", - "idpErrorOidcTokenValidating": "Error validating OIDC token", - "idpConnectingTo": "Connecting to {name}", - "idpConnectingToDescription": "Validating your identity", - "idpConnectingToProcess": "Connecting...", - "idpConnectingToFinished": "Connected", - "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", - "idpErrorNotFound": "IdP not found", + "pangolinLogoAlt": "Лого на Pangolin", + "inviteAlready": "Изглежда, че сте били поканени!", + "inviteAlreadyDescription": "За да приемете поканата, трябва да влезете или да създадете акаунт.", + "signupQuestion": "Вече имате акаунт?", + "login": "Влизане", + "resourceNotFound": "Ресурсът не е намерен", + "resourceNotFoundDescription": "Ресурсът, който се опитвате да достъпите, не съществува.", + "pincodeRequirementsLength": "ПИН трябва да бъде точно 6 цифри", + "pincodeRequirementsChars": "ПИН трябва да съдържа само цифри", + "passwordRequirementsLength": "Паролата трябва да бъде поне 1 символа дълга", + "passwordRequirementsTitle": "Изисквания към паролата:", + "passwordRequirementLength": "Поне 8 символа дължина", + "passwordRequirementUppercase": "Поне една главна буква", + "passwordRequirementLowercase": "Поне една малка буква", + "passwordRequirementNumber": "Поне една цифра", + "passwordRequirementSpecial": "Поне един специален символ", + "passwordRequirementsMet": "✓ Паролата отговаря на всички изисквания", + "passwordStrength": "Сила на паролата", + "passwordStrengthWeak": "Слаба", + "passwordStrengthMedium": "Средна", + "passwordStrengthStrong": "Силна", + "passwordRequirements": "Изисквания:", + "passwordRequirementLengthText": "8+ символа", + "passwordRequirementUppercaseText": "Главна буква (A-Z)", + "passwordRequirementLowercaseText": "Малка буква (a-z)", + "passwordRequirementNumberText": "Цифра (0-9)", + "passwordRequirementSpecialText": "Специален символ (!@#$%...)", + "passwordsDoNotMatch": "Паролите не съвпадат", + "otpEmailRequirementsLength": "OTP трябва да бъде поне 1 символа дълга", + "otpEmailSent": "OTP изпратен", + "otpEmailSentDescription": "OTP беше изпратен на вашия имейл", + "otpEmailErrorAuthenticate": "Неуспешно удостоверяване чрез имейл", + "pincodeErrorAuthenticate": "Неуспешно удостоверяване чрез ПИН", + "passwordErrorAuthenticate": "Неуспешно удостоверяване чрез парола", + "poweredBy": "Поддържано от", + "authenticationRequired": "Необходимо е удостоверяване", + "authenticationMethodChoose": "Изберете предпочитаният метод за достъп до {name}", + "authenticationRequest": "Трябва да удостоверите за достъп до {name}", + "user": "Потребител", + "pincodeInput": "6-цифрен ПИН код", + "pincodeSubmit": "Влез с ПИН", + "passwordSubmit": "Влез с Парола", + "otpEmailDescription": "Код за еднократна употреба ще бъде изпратен на този имейл.", + "otpEmailSend": "Изпращане на код за еднократна употреба", + "otpEmail": "Парола за еднократна употреба (OTP)", + "otpEmailSubmit": "Изпрати OTP", + "backToEmail": "Назад към Имейл", + "noSupportKey": "Сървърът работи без поддържащ ключ. Разгледайте възможностите за подкрепа на проекта!", + "accessDenied": "Достъпът е отказан", + "accessDeniedDescription": "Не ви е разрешен достъпът до този ресурс. Ако това е грешка, моля свържете се с администратора.", + "accessTokenError": "Грешка при проверка на достъпния токен", + "accessGranted": "Достъпът е разрешен", + "accessUrlInvalid": "Невалиден URL за достъп", + "accessGrantedDescription": "Достъпът до този ресурс ви е разрешен. Пренасочване...", + "accessUrlInvalidDescription": "Този споделен URL за достъп е невалиден. Моля, свържете се със собственика на ресурса за нов URL.", + "tokenInvalid": "Невалиден токен", + "pincodeInvalid": "Невалиден код", + "passwordErrorRequestReset": "Неуспешно заявление за нулиране:", + "passwordErrorReset": "Неуспешно нулиране на паролата:", + "passwordResetSuccess": "Паролата е успешно нулирана! Връщане към вход...", + "passwordReset": "Нулиране на парола", + "passwordResetDescription": "Следвайте стъпките, за да нулирате паролата си", + "passwordResetSent": "Ще изпратим код за нулиране на паролата на този имейл адрес.", + "passwordResetCode": "Код за нулиране", + "passwordResetCodeDescription": "Проверете имейла си за код за нулиране.", + "passwordNew": "Нова парола", + "passwordNewConfirm": "Потвърдете новата парола", + "pincodeAuth": "Код на удостоверителя", + "pincodeSubmit2": "Изпрати код", + "passwordResetSubmit": "Заявка за нулиране", + "passwordBack": "Назад към Парола", + "loginBack": "Връщане към вход", + "signup": "Регистрация", + "loginStart": "Влезте, за да започнете", + "idpOidcTokenValidating": "Валидиране на OIDC токен", + "idpOidcTokenResponse": "Валидирайте отговора на OIDC токен", + "idpErrorOidcTokenValidating": "Грешка при валидиране на OIDC токен", + "idpConnectingTo": "Свързване с {name}", + "idpConnectingToDescription": "Валидиране на идентичността ви", + "idpConnectingToProcess": "Свързва се...", + "idpConnectingToFinished": "Свързано", + "idpErrorConnectingTo": "Имаше проблем със свързването към {name}. Моля, свържете се с вашия администратор.", + "idpErrorNotFound": "Не е намерен идентификационен доставчик", "idpGoogleAlt": "Google", "idpAzureAlt": "Azure", - "inviteInvalid": "Invalid Invite", - "inviteInvalidDescription": "The invite link is invalid.", - "inviteErrorWrongUser": "Invite is not for this user", - "inviteErrorUserNotExists": "User does not exist. Please create an account first.", - "inviteErrorLoginRequired": "You must be logged in to accept an invite", - "inviteErrorExpired": "The invite may have expired", - "inviteErrorRevoked": "The invite might have been revoked", - "inviteErrorTypo": "There could be a typo in the invite link", - "pangolinSetup": "Setup - Pangolin", - "orgNameRequired": "Organization name is required", - "orgIdRequired": "Organization ID is required", - "orgErrorCreate": "An error occurred while creating org", - "pageNotFound": "Page Not Found", - "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", - "overview": "Overview", - "home": "Home", - "accessControl": "Access Control", - "settings": "Settings", - "usersAll": "All Users", - "license": "License", - "pangolinDashboard": "Dashboard - Pangolin", - "noResults": "No results found.", + "inviteInvalid": "Невалидна покана", + "inviteInvalidDescription": "Линкът към поканата е невалиден.", + "inviteErrorWrongUser": "Поканата не е за този потребител", + "inviteErrorUserNotExists": "Потребителят не съществува. Моля, създайте акаунт първо.", + "inviteErrorLoginRequired": "Трябва да сте влезли, за да приемете покана", + "inviteErrorExpired": "Може би поканата е изтекла", + "inviteErrorRevoked": "Поканата може да е била отменена", + "inviteErrorTypo": "Може би има грешка при въвеждане в линка за поканата", + "pangolinSetup": "Настройка - Pangolin", + "orgNameRequired": "Името на организацията е задължително", + "orgIdRequired": "ID на организацията е задължително", + "orgErrorCreate": "Възникна грешка при създаване на организация", + "pageNotFound": "Страницата не е намерена", + "pageNotFoundDescription": "О, не! Страницата, която търсите, не съществува.", + "overview": "Общ преглед", + "home": "Начало", + "accessControl": "Контрол на достъпа", + "settings": "Настройки", + "usersAll": "Всички потребители", + "license": "Лиценз", + "pangolinDashboard": "Табло за управление - Pangolin", + "noResults": "Няма намерени резултати.", "terabytes": "{count} TB", "gigabytes": "{count} GB", "megabytes": "{count} MB", - "tagsEntered": "Entered Tags", - "tagsEnteredDescription": "These are the tags you`ve entered.", - "tagsWarnCannotBeLessThanZero": "maxTags and minTags cannot be less than 0", - "tagsWarnNotAllowedAutocompleteOptions": "Tag not allowed as per autocomplete options", - "tagsWarnInvalid": "Invalid tag as per validateTag", - "tagWarnTooShort": "Tag {tagText} is too short", - "tagWarnTooLong": "Tag {tagText} is too long", - "tagsWarnReachedMaxNumber": "Reached the maximum number of tags allowed", - "tagWarnDuplicate": "Duplicate tag {tagText} not added", - "supportKeyInvalid": "Invalid Key", - "supportKeyInvalidDescription": "Your supporter key is invalid.", - "supportKeyValid": "Valid Key", - "supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!", - "supportKeyErrorValidationDescription": "Failed to validate supporter key.", - "supportKey": "Support Development and Adopt a Pangolin!", - "supportKeyDescription": "Purchase a supporter key to help us continue developing Pangolin for the community. Your contribution allows us to commit more time to maintain and add new features to the application for everyone. We will never use this to paywall features. This is separate from any Commercial Edition.", - "supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!", - "supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on", - "supportKeyPurchaseLink": "our website", - "supportKeyPurchase2": "and redeem it here.", - "supportKeyLearnMore": "Learn more.", - "supportKeyOptions": "Please select the option that best suits you.", - "supportKetOptionFull": "Full Supporter", - "forWholeServer": "For the whole server", - "lifetimePurchase": "Lifetime purchase", - "supporterStatus": "Supporter status", - "buy": "Buy", - "supportKeyOptionLimited": "Limited Supporter", - "forFiveUsers": "For 5 or less users", - "supportKeyRedeem": "Redeem Supporter Key", - "supportKeyHideSevenDays": "Hide for 7 days", - "supportKeyEnter": "Enter Supporter Key", - "supportKeyEnterDescription": "Meet your very own pet Pangolin!", - "githubUsername": "GitHub Username", - "supportKeyInput": "Supporter Key", - "supportKeyBuy": "Buy Supporter Key", - "logoutError": "Error logging out", - "signingAs": "Signed in as", - "serverAdmin": "Server Admin", - "managedSelfhosted": "Managed Self-Hosted", - "otpEnable": "Enable Two-factor", - "otpDisable": "Disable Two-factor", - "logout": "Log Out", - "licenseTierProfessionalRequired": "Professional Edition Required", - "licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.", - "actionGetOrg": "Get Organization", - "updateOrgUser": "Update Org User", - "createOrgUser": "Create Org User", - "actionUpdateOrg": "Update Organization", - "actionUpdateUser": "Update User", - "actionGetUser": "Get User", - "actionGetOrgUser": "Get Organization User", - "actionListOrgDomains": "List Organization Domains", - "actionCreateSite": "Create Site", - "actionDeleteSite": "Delete Site", - "actionGetSite": "Get Site", - "actionListSites": "List Sites", - "actionApplyBlueprint": "Apply Blueprint", - "setupToken": "Setup Token", - "setupTokenDescription": "Enter the setup token from the server console.", - "setupTokenRequired": "Setup token is required", - "actionUpdateSite": "Update Site", - "actionListSiteRoles": "List Allowed Site Roles", - "actionCreateResource": "Create Resource", - "actionDeleteResource": "Delete Resource", - "actionGetResource": "Get Resource", - "actionListResource": "List Resources", - "actionUpdateResource": "Update Resource", - "actionListResourceUsers": "List Resource Users", - "actionSetResourceUsers": "Set Resource Users", - "actionSetAllowedResourceRoles": "Set Allowed Resource Roles", - "actionListAllowedResourceRoles": "List Allowed Resource Roles", - "actionSetResourcePassword": "Set Resource Password", - "actionSetResourcePincode": "Set Resource Pincode", - "actionSetResourceEmailWhitelist": "Set Resource Email Whitelist", - "actionGetResourceEmailWhitelist": "Get Resource Email Whitelist", - "actionCreateTarget": "Create Target", - "actionDeleteTarget": "Delete Target", - "actionGetTarget": "Get Target", - "actionListTargets": "List Targets", - "actionUpdateTarget": "Update Target", - "actionCreateRole": "Create Role", - "actionDeleteRole": "Delete Role", - "actionGetRole": "Get Role", - "actionListRole": "List Roles", - "actionUpdateRole": "Update Role", - "actionListAllowedRoleResources": "List Allowed Role Resources", - "actionInviteUser": "Invite User", - "actionRemoveUser": "Remove User", - "actionListUsers": "List Users", - "actionAddUserRole": "Add User Role", - "actionGenerateAccessToken": "Generate Access Token", - "actionDeleteAccessToken": "Delete Access Token", - "actionListAccessTokens": "List Access Tokens", - "actionCreateResourceRule": "Create Resource Rule", - "actionDeleteResourceRule": "Delete Resource Rule", - "actionListResourceRules": "List Resource Rules", - "actionUpdateResourceRule": "Update Resource Rule", - "actionListOrgs": "List Organizations", - "actionCheckOrgId": "Check ID", - "actionCreateOrg": "Create Organization", - "actionDeleteOrg": "Delete Organization", - "actionListApiKeys": "List API Keys", - "actionListApiKeyActions": "List API Key Actions", - "actionSetApiKeyActions": "Set API Key Allowed Actions", - "actionCreateApiKey": "Create API Key", - "actionDeleteApiKey": "Delete API Key", - "actionCreateIdp": "Create IDP", - "actionUpdateIdp": "Update IDP", - "actionDeleteIdp": "Delete IDP", - "actionListIdps": "List IDP", - "actionGetIdp": "Get IDP", - "actionCreateIdpOrg": "Create IDP Org Policy", - "actionDeleteIdpOrg": "Delete IDP Org Policy", - "actionListIdpOrgs": "List IDP Orgs", - "actionUpdateIdpOrg": "Update IDP Org", - "actionCreateClient": "Create Client", - "actionDeleteClient": "Delete Client", - "actionUpdateClient": "Update Client", - "actionListClients": "List Clients", - "actionGetClient": "Get Client", - "actionCreateSiteResource": "Create Site Resource", - "actionDeleteSiteResource": "Delete Site Resource", - "actionGetSiteResource": "Get Site Resource", - "actionListSiteResources": "List Site Resources", - "actionUpdateSiteResource": "Update Site Resource", - "actionListInvitations": "List Invitations", - "noneSelected": "None selected", - "orgNotFound2": "No organizations found.", - "searchProgress": "Search...", - "create": "Create", - "orgs": "Organizations", - "loginError": "An error occurred while logging in", - "passwordForgot": "Forgot your password?", - "otpAuth": "Two-Factor Authentication", - "otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.", - "otpAuthSubmit": "Submit Code", - "idpContinue": "Or continue with", - "otpAuthBack": "Back to Log In", - "navbar": "Navigation Menu", - "navbarDescription": "Main navigation menu for the application", - "navbarDocsLink": "Documentation", - "commercialEdition": "Commercial Edition", - "otpErrorEnable": "Unable to enable 2FA", - "otpErrorEnableDescription": "An error occurred while enabling 2FA", - "otpSetupCheckCode": "Please enter a 6-digit code", - "otpSetupCheckCodeRetry": "Invalid code. Please try again.", - "otpSetup": "Enable Two-factor Authentication", - "otpSetupDescription": "Secure your account with an extra layer of protection", - "otpSetupScanQr": "Scan this QR code with your authenticator app or enter the secret key manually:", - "otpSetupSecretCode": "Authenticator Code", - "otpSetupSuccess": "Two-Factor Authentication Enabled", - "otpSetupSuccessStoreBackupCodes": "Your account is now more secure. Don't forget to save your backup codes.", - "otpErrorDisable": "Unable to disable 2FA", - "otpErrorDisableDescription": "An error occurred while disabling 2FA", - "otpRemove": "Disable Two-factor Authentication", - "otpRemoveDescription": "Disable two-factor authentication for your account", - "otpRemoveSuccess": "Two-Factor Authentication Disabled", - "otpRemoveSuccessMessage": "Two-factor authentication has been disabled for your account. You can enable it again at any time.", - "otpRemoveSubmit": "Disable 2FA", - "paginator": "Page {current} of {last}", - "paginatorToFirst": "Go to first page", - "paginatorToPrevious": "Go to previous page", - "paginatorToNext": "Go to next page", - "paginatorToLast": "Go to last page", - "copyText": "Copy text", - "copyTextFailed": "Failed to copy text: ", - "copyTextClipboard": "Copy to clipboard", - "inviteErrorInvalidConfirmation": "Invalid confirmation", - "passwordRequired": "Password is required", - "allowAll": "Allow All", - "permissionsAllowAll": "Allow All Permissions", - "githubUsernameRequired": "GitHub username is required", - "supportKeyRequired": "Supporter key is required", - "passwordRequirementsChars": "Password must be at least 8 characters", - "language": "Language", - "verificationCodeRequired": "Code is required", - "userErrorNoUpdate": "No user to update", - "siteErrorNoUpdate": "No site to update", - "resourceErrorNoUpdate": "No resource to update", - "authErrorNoUpdate": "No auth info to update", - "orgErrorNoUpdate": "No org to update", - "orgErrorNoProvided": "No org provided", - "apiKeysErrorNoUpdate": "No API key to update", - "sidebarOverview": "Overview", - "sidebarHome": "Home", - "sidebarSites": "Sites", - "sidebarResources": "Resources", - "sidebarAccessControl": "Access Control", - "sidebarUsers": "Users", - "sidebarInvitations": "Invitations", - "sidebarRoles": "Roles", - "sidebarShareableLinks": "Shareable Links", - "sidebarApiKeys": "API Keys", - "sidebarSettings": "Settings", - "sidebarAllUsers": "All Users", - "sidebarIdentityProviders": "Identity Providers", - "sidebarLicense": "License", - "sidebarClients": "Clients (Beta)", - "sidebarDomains": "Domains", - "enableDockerSocket": "Enable Docker Blueprint", - "enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.", - "enableDockerSocketLink": "Learn More", - "viewDockerContainers": "View Docker Containers", - "containersIn": "Containers in {siteName}", - "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", - "containerName": "Name", - "containerImage": "Image", - "containerState": "State", - "containerNetworks": "Networks", - "containerHostnameIp": "Hostname/IP", - "containerLabels": "Labels", - "containerLabelsCount": "{count, plural, one {# label} other {# labels}}", - "containerLabelsTitle": "Container Labels", - "containerLabelEmpty": "", - "containerPorts": "Ports", - "containerPortsMore": "+{count} more", - "containerActions": "Actions", - "select": "Select", - "noContainersMatchingFilters": "No containers found matching the current filters.", - "showContainersWithoutPorts": "Show containers without ports", - "showStoppedContainers": "Show stopped containers", - "noContainersFound": "No containers found. Make sure Docker containers are running.", - "searchContainersPlaceholder": "Search across {count} containers...", - "searchResultsCount": "{count, plural, one {# result} other {# results}}", - "filters": "Filters", - "filterOptions": "Filter Options", - "filterPorts": "Ports", - "filterStopped": "Stopped", - "clearAllFilters": "Clear all filters", - "columns": "Columns", - "toggleColumns": "Toggle Columns", - "refreshContainersList": "Refresh containers list", - "searching": "Searching...", - "noContainersFoundMatching": "No containers found matching \"{filter}\".", - "light": "light", - "dark": "dark", - "system": "system", - "theme": "Theme", - "subnetRequired": "Subnet is required", - "initialSetupTitle": "Initial Server Setup", - "initialSetupDescription": "Create the intial server admin account. Only one server admin can exist. You can always change these credentials later.", - "createAdminAccount": "Create Admin Account", - "setupErrorCreateAdmin": "An error occurred while creating the server admin account.", - "certificateStatus": "Certificate Status", - "loading": "Loading", - "restart": "Restart", - "domains": "Domains", - "domainsDescription": "Manage domains for your organization", - "domainsSearch": "Search domains...", - "domainAdd": "Add Domain", - "domainAddDescription": "Register a new domain with your organization", - "domainCreate": "Create Domain", - "domainCreatedDescription": "Domain created successfully", - "domainDeletedDescription": "Domain deleted successfully", - "domainQuestionRemove": "Are you sure you want to remove the domain {domain} from your account?", - "domainMessageRemove": "Once removed, the domain will no longer be associated with your account.", - "domainMessageConfirm": "To confirm, please type the domain name below.", - "domainConfirmDelete": "Confirm Delete Domain", - "domainDelete": "Delete Domain", - "domain": "Domain", - "selectDomainTypeNsName": "Domain Delegation (NS)", - "selectDomainTypeNsDescription": "This domain and all its subdomains. Use this when you want to control an entire domain zone.", - "selectDomainTypeCnameName": "Single Domain (CNAME)", - "selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.", - "selectDomainTypeWildcardName": "Wildcard Domain", - "selectDomainTypeWildcardDescription": "This domain and its subdomains.", - "domainDelegation": "Single Domain", - "selectType": "Select a type", - "actions": "Actions", - "refresh": "Refresh", - "refreshError": "Failed to refresh data", - "verified": "Verified", - "pending": "Pending", - "sidebarBilling": "Billing", - "billing": "Billing", - "orgBillingDescription": "Manage your billing information and subscriptions", + "tagsEntered": "Въведени тагове", + "tagsEnteredDescription": "Това са таговете, които сте въвели.", + "tagsWarnCannotBeLessThanZero": "maxTags и minTags не могат да бъдат по-малки от 0", + "tagsWarnNotAllowedAutocompleteOptions": "Таг не е разрешен, съобразно опциите за автозавършване", + "tagsWarnInvalid": "Невалиден таг според validateTag", + "tagWarnTooShort": "Таг {tagText} е твърде кратък", + "tagWarnTooLong": "Таг {tagText} е твърде дълъг", + "tagsWarnReachedMaxNumber": "Достигнат е максималния брой разрешени тагове", + "tagWarnDuplicate": "Дублиран таг {tagText} не е добавен", + "supportKeyInvalid": "Невалиден ключ", + "supportKeyInvalidDescription": "Вашият поддържащ ключ е невалиден.", + "supportKeyValid": "Валиден ключ", + "supportKeyValidDescription": "Вашият поддържащ ключ е валидиран. Благодарим за подкрепата!", + "supportKeyErrorValidationDescription": "Неуспешна валидиция на поддържащ ключ.", + "supportKey": "Подкрепете развитието и си осинови Панголин!", + "supportKeyDescription": "Купете поддържащ ключ, за да ни помогнете да продължим развитието на Pangolin за общността. Вашата помощ ни позволява да отделим повече време за поддръжка и добавяне на нови функции към приложението за всички. Ние никога няма да използваме това за заплащане на функции. Това е отделно от каквото и да е издание за комерсиални цели.", + "supportKeyPet": "Вие също ще имате възможност да осиновите и срещнете вашия собствен домашен панголин!", + "supportKeyPurchase": "Плащания се обработват през GitHub. След това можете да получите вашия ключ от", + "supportKeyPurchaseLink": "нашия уебсайт", + "supportKeyPurchase2": "и да го използвате тук.", + "supportKeyLearnMore": "Научете повече.", + "supportKeyOptions": "Изберете опцията, която най-добре съответства на вас.", + "supportKetOptionFull": "Пълна поддръжка", + "forWholeServer": "За целия сървър", + "lifetimePurchase": "Пожизнена покупка", + "supporterStatus": "Статус на поддръжника", + "buy": "Купи", + "supportKeyOptionLimited": "Ограничена поддръжка", + "forFiveUsers": "За 5 или по-малко потребители", + "supportKeyRedeem": "Изкупи поддържащ ключ", + "supportKeyHideSevenDays": "Скрий за 7 дни", + "supportKeyEnter": "Въведете поддържащ ключ", + "supportKeyEnterDescription": "Запознайте се с вашия собствен домашен панголин!", + "githubUsername": "Потребителско име в GitHub", + "supportKeyInput": "Поддържащ ключ", + "supportKeyBuy": "Купи поддържащ ключ", + "logoutError": "Грешка при излизане", + "signingAs": "Влезли сте като", + "serverAdmin": "Администратор на сървъра", + "managedSelfhosted": "Управлявано Самостоятелно-хоствано", + "otpEnable": "Включване на двуфакторен", + "otpDisable": "Изключване на двуфакторен", + "logout": "Изход", + "licenseTierProfessionalRequired": "Изисква се професионално издание", + "licenseTierProfessionalRequiredDescription": "Тази функция е достъпна само в професионалното издание.", + "actionGetOrg": "Вземете организация", + "updateOrgUser": "Актуализиране на Организационна Потребител", + "createOrgUser": "Създаване на Организационна Потребител", + "actionUpdateOrg": "Актуализиране на организацията", + "actionUpdateUser": "Актуализиране на потребител", + "actionGetUser": "Получаване на потребител", + "actionGetOrgUser": "Вземете потребител на организация", + "actionListOrgDomains": "Изброяване на домейни на организация", + "actionCreateSite": "Създаване на сайт", + "actionDeleteSite": "Изтриване на сайта", + "actionGetSite": "Вземете сайт", + "actionListSites": "Изброяване на сайтове", + "actionApplyBlueprint": "Приложи Чернова", + "setupToken": "Конфигурация на токен", + "setupTokenDescription": "Въведете конфигурационния токен от сървърната конзола.", + "setupTokenRequired": "Необходим е конфигурационен токен", + "actionUpdateSite": "Актуализиране на сайт", + "actionListSiteRoles": "Изброяване на позволените роли за сайта", + "actionCreateResource": "Създаване на ресурс", + "actionDeleteResource": "Изтриване на ресурс", + "actionGetResource": "Вземете ресурс", + "actionListResource": "Изброяване на ресурси", + "actionUpdateResource": "Актуализиране на ресурс", + "actionListResourceUsers": "Изброяване на потребители на ресурси", + "actionSetResourceUsers": "Задайте потребители на ресурси", + "actionSetAllowedResourceRoles": "Задайте позволени роли за ресурса", + "actionListAllowedResourceRoles": "Изброяване на позволени роли за ресурси", + "actionSetResourcePassword": "Задайте парола на ресурса", + "actionSetResourcePincode": "Задайте ПИН код на ресурса", + "actionSetResourceEmailWhitelist": "Задайте списък на одобрените имейл адреси за ресурса", + "actionGetResourceEmailWhitelist": "Вземете списък на одобрените имейл адреси за ресурса", + "actionCreateTarget": "Създайте цел", + "actionDeleteTarget": "Изтрийте цел", + "actionGetTarget": "Вземете цел", + "actionListTargets": "Изброяване на цели", + "actionUpdateTarget": "Актуализирайте цел", + "actionCreateRole": "Създайте роля", + "actionDeleteRole": "Изтрийте роля", + "actionGetRole": "Вземете роля", + "actionListRole": "Изброяване на роли", + "actionUpdateRole": "Актуализирайте роля", + "actionListAllowedRoleResources": "Изброяване на разрешени ресурси за роля", + "actionInviteUser": "Покани потребител", + "actionRemoveUser": "Изтрийте потребител", + "actionListUsers": "Изброяване на потребители", + "actionAddUserRole": "Добавяне на роля на потребител", + "actionGenerateAccessToken": "Генериране на токен за достъп", + "actionDeleteAccessToken": "Изтриване на токен за достъп", + "actionListAccessTokens": "Изброяване на токени за достъп", + "actionCreateResourceRule": "Създаване на правило за ресурс", + "actionDeleteResourceRule": "Изтрийте правило за ресурс", + "actionListResourceRules": "Изброяване на правила за ресурс", + "actionUpdateResourceRule": "Актуализиране на правило за ресурс", + "actionListOrgs": "Изброяване на организации", + "actionCheckOrgId": "Проверка на ID на организацията", + "actionCreateOrg": "Създаване на организация", + "actionDeleteOrg": "Изтриване на организация", + "actionListApiKeys": "Изброяване на API ключове", + "actionListApiKeyActions": "Изброяване на действия за API ключ", + "actionSetApiKeyActions": "Задайте разрешени действия за API ключ", + "actionCreateApiKey": "Създаване на API ключ", + "actionDeleteApiKey": "Изтриване на API ключ", + "actionCreateIdp": "Създаване на IdP", + "actionUpdateIdp": "Актуализиране на IdP", + "actionDeleteIdp": "Изтриване на IdP", + "actionListIdps": "Изброяване на IdP", + "actionGetIdp": "Вземете IdP", + "actionCreateIdpOrg": "Създаване на политика за IdP организация", + "actionDeleteIdpOrg": "Изтриване на политика за IdP организация", + "actionListIdpOrgs": "Изброяване на IdP организации", + "actionUpdateIdpOrg": "Актуализиране на IdP организация", + "actionCreateClient": "Създаване на клиент", + "actionDeleteClient": "Изтриване на клиент", + "actionUpdateClient": "Актуализиране на клиент", + "actionListClients": "Списък с клиенти", + "actionGetClient": "Получаване на клиент", + "actionCreateSiteResource": "Създаване на сайт ресурс", + "actionDeleteSiteResource": "Изтриване на сайт ресурс", + "actionGetSiteResource": "Получаване на сайт ресурс", + "actionListSiteResources": "Списък на ресурсите на сайта", + "actionUpdateSiteResource": "Актуализиране на сайт ресурс", + "actionListInvitations": "Списък с покани", + "noneSelected": "Нищо не е избрано", + "orgNotFound2": "Няма намерени организации.", + "searchProgress": "Търсене...", + "create": "Създаване", + "orgs": "Организации", + "loginError": "Възникна грешка при влизане", + "passwordForgot": "Забравена парола?", + "otpAuth": "Двуфакторно удостоверяване", + "otpAuthDescription": "Въведете кода от приложението за удостоверяване или един от вашите резервни кодове за еднократна употреба.", + "otpAuthSubmit": "Изпрати код", + "idpContinue": "Или продължете със", + "otpAuthBack": "Назад към Вход", + "navbar": "Навигационно меню", + "navbarDescription": "Главно навигационно меню за приложението", + "navbarDocsLink": "Документация", + "commercialEdition": "Търговско издание", + "otpErrorEnable": "Не може да се активира 2FA", + "otpErrorEnableDescription": "Възникна грешка при активиране на 2FA", + "otpSetupCheckCode": "Моля, въведете 6-цифрен код", + "otpSetupCheckCodeRetry": "Невалиден код. Моля, опитайте отново.", + "otpSetup": "Активиране на двуфакторно удостоверяване", + "otpSetupDescription": "Защитете профила си с допълнителен слой защита", + "otpSetupScanQr": "Сканирайте този QR код с вашето приложение за автентикация или въведете ръчно тайния ключ:", + "otpSetupSecretCode": "Код за автентикация", + "otpSetupSuccess": "Двуфакторното удостоверяване е активирано", + "otpSetupSuccessStoreBackupCodes": "Профилът ви сега е по-сигурен. Не забравяйте да запазите резервните си кодове.", + "otpErrorDisable": "Не може да се деактивира 2FA", + "otpErrorDisableDescription": "Възникна грешка при деактивиране на 2FA", + "otpRemove": "Деактивиране на двуфакторно удостоверяване", + "otpRemoveDescription": "Деактивирайте двуфакторното удостоверяване за вашия профил", + "otpRemoveSuccess": "Двуфакторното удостоверяване е деактивирано", + "otpRemoveSuccessMessage": "Двуфакторното удостоверяване за вашия профил е деактивирано. Можете да го активирате отново по всяко време.", + "otpRemoveSubmit": "Деактивиране на 2FA", + "paginator": "Страница {current} от {last}", + "paginatorToFirst": "Отидете на първата страница", + "paginatorToPrevious": "Отидете на предишната страница", + "paginatorToNext": "Отидете на следващата страница", + "paginatorToLast": "Отидете на последната страница", + "copyText": "Копиране на текст", + "copyTextFailed": "Неуспешно копиране на текст: ", + "copyTextClipboard": "Копиране в клипборда", + "inviteErrorInvalidConfirmation": "Невалидно потвърждение", + "passwordRequired": "Паролата е задължителна", + "allowAll": "Разрешаване на всички", + "permissionsAllowAll": "Разрешаване на всички разрешения", + "githubUsernameRequired": "GitHub потребителското име е задължително", + "supportKeyRequired": "Ключът на поддръжката е задължителен", + "passwordRequirementsChars": "Паролата трябва да е поне 8 символа", + "language": "Език", + "verificationCodeRequired": "Кодът е задължителен", + "userErrorNoUpdate": "Няма потребител за актуализиране", + "siteErrorNoUpdate": "Няма сайт за актуализиране", + "resourceErrorNoUpdate": "Няма ресурс за актуализиране", + "authErrorNoUpdate": "Няма информация за удостоверяване за актуализиране", + "orgErrorNoUpdate": "Няма организация за актуализиране", + "orgErrorNoProvided": "Няма предоставена организация", + "apiKeysErrorNoUpdate": "Няма API ключ за актуализиране", + "sidebarOverview": "Общ преглед", + "sidebarHome": "Начало", + "sidebarSites": "Сайтове", + "sidebarResources": "Ресурси", + "sidebarAccessControl": "Контрол на достъпа", + "sidebarUsers": "Потребители", + "sidebarInvitations": "Покани", + "sidebarRoles": "Роли", + "sidebarShareableLinks": "Споделени връзки", + "sidebarApiKeys": "API ключове", + "sidebarSettings": "Настройки", + "sidebarAllUsers": "Всички потребители", + "sidebarIdentityProviders": "Идентификационни доставчици", + "sidebarLicense": "Лиценз", + "sidebarClients": "Клиенти (Бета)", + "sidebarDomains": "Домейни", + "enableDockerSocket": "Активиране на Docker Чернова", + "enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.", + "enableDockerSocketLink": "Научете повече", + "viewDockerContainers": "Преглед на Docker контейнери", + "containersIn": "Контейнери в {siteName}", + "selectContainerDescription": "Изберете контейнер, който да ползвате като име на хост за целта. Натиснете порт, за да ползвате порт", + "containerName": "Име", + "containerImage": "Образ", + "containerState": "Състояние", + "containerNetworks": "Мрежи", + "containerHostnameIp": "Име на хост/IP", + "containerLabels": "Етикети", + "containerLabelsCount": "{count, plural, one {# етикет} other {# етикета}}", + "containerLabelsTitle": "Етикети на контейнера", + "containerLabelEmpty": "<празно>", + "containerPorts": "Портове", + "containerPortsMore": "+{count} още", + "containerActions": "Действия", + "select": "Избор", + "noContainersMatchingFilters": "Не са намерени контейнери, съответстващи на текущите филтри.", + "showContainersWithoutPorts": "Показване на контейнери без портове", + "showStoppedContainers": "Показване на спрени контейнери", + "noContainersFound": "Не са намерени контейнери. Уверете се, че Docker контейнерите са активирани.", + "searchContainersPlaceholder": "Търсене сред {count} контейнери...", + "searchResultsCount": "{count, plural, one {# резултат} other {# резултати}}", + "filters": "Филтри", + "filterOptions": "Опции за филтриране", + "filterPorts": "Портове", + "filterStopped": "Спрени", + "clearAllFilters": "Изчистване на всички филтри", + "columns": "Колони", + "toggleColumns": "Превключване на колони", + "refreshContainersList": "Обновяване на списъка с контейнери", + "searching": "Търсене...", + "noContainersFoundMatching": "Не са намерени контейнери, съответстващи на \"{filter}\".", + "light": "светъл", + "dark": "тъмен", + "system": "система", + "theme": "Тема", + "subnetRequired": "Необходим е подмрежа", + "initialSetupTitle": "Начална конфигурация на сървъра", + "initialSetupDescription": "Създайте администраторски акаунт на сървъра. Може да съществува само един администраторски акаунт. Винаги можете да промените тези данни по-късно.", + "createAdminAccount": "Създаване на админ акаунт", + "setupErrorCreateAdmin": "Възникна грешка при създаване на админ акаунт.", + "certificateStatus": "Статус на сертификата", + "loading": "Зареждане", + "restart": "Рестарт", + "domains": "Домейни", + "domainsDescription": "Управление на домейни за вашата организация", + "domainsSearch": "Търсене на домейни...", + "domainAdd": "Добавяне на домейн", + "domainAddDescription": "Регистриране на нов домейн с вашата организация", + "domainCreate": "Създаване на домейн", + "domainCreatedDescription": "Домейнът е създаден успешно", + "domainDeletedDescription": "Домейнът е изтрит успешно", + "domainQuestionRemove": "Сигурни ли сте, че искате да премахнете домейна {domain} от вашия профил?", + "domainMessageRemove": "След премахването, домейнът вече няма да бъде свързан с вашия профил.", + "domainMessageConfirm": "За потвърждение, моля въведете името на домейна по-долу.", + "domainConfirmDelete": "Потвърдете изтриването на домейн", + "domainDelete": "Изтриване на домейн", + "domain": "Домейн", + "selectDomainTypeNsName": "Делегация на домейни (NS)", + "selectDomainTypeNsDescription": "Този домейн и всичките му поддомейни. Ползвайте го, когато искате да контролирате цялата зона на домейна.", + "selectDomainTypeCnameName": "Единичен домейн (CNAME)", + "selectDomainTypeCnameDescription": "Само този специфичен домейн. Ползвайте го за индивидуални поддомейни или специфични домейн записи.", + "selectDomainTypeWildcardName": "Общ домейн", + "selectDomainTypeWildcardDescription": "Този домейн и неговите поддомейни.", + "domainDelegation": "Единичен домейн", + "selectType": "Изберете тип", + "actions": "Действия", + "refresh": "Обновяване", + "refreshError": "Неуспешно обновяване на данни", + "verified": "Потвърдено", + "pending": "Чакащо", + "sidebarBilling": "Фактуриране", + "billing": "Фактуриране", + "orgBillingDescription": "Управление на информацията за фактуриране и абонаментите", "github": "GitHub", - "pangolinHosted": "Pangolin Hosted", + "pangolinHosted": "Hosted Pangolin", "fossorial": "Fossorial", - "completeAccountSetup": "Complete Account Setup", - "completeAccountSetupDescription": "Set your password to get started", - "accountSetupSent": "We'll send an account setup code to this email address.", - "accountSetupCode": "Setup Code", - "accountSetupCodeDescription": "Check your email for the setup code.", - "passwordCreate": "Create Password", - "passwordCreateConfirm": "Confirm Password", - "accountSetupSubmit": "Send Setup Code", - "completeSetup": "Complete Setup", - "accountSetupSuccess": "Account setup completed! Welcome to Pangolin!", - "documentation": "Documentation", - "saveAllSettings": "Save All Settings", - "settingsUpdated": "Settings updated", - "settingsUpdatedDescription": "All settings have been updated successfully", - "settingsErrorUpdate": "Failed to update settings", - "settingsErrorUpdateDescription": "An error occurred while updating settings", - "sidebarCollapse": "Collapse", - "sidebarExpand": "Expand", - "newtUpdateAvailable": "Update Available", - "newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.", - "domainPickerEnterDomain": "Domain", + "completeAccountSetup": "Завършете настройката на профила", + "completeAccountSetupDescription": "Задайте паролата си, за да започнете", + "accountSetupSent": "Ще изпратим код за настройка на профила на този адрес имейл.", + "accountSetupCode": "Код за настройка", + "accountSetupCodeDescription": "Проверете имейла си за код за настройка.", + "passwordCreate": "Създайте парола", + "passwordCreateConfirm": "Потвърждение на паролата", + "accountSetupSubmit": "Изпращане на код за настройка", + "completeSetup": "Завършване на настройката", + "accountSetupSuccess": "Настройката на профила завърши успешно! Добре дошли в Pangolin!", + "documentation": "Документация", + "saveAllSettings": "Запазване на всички настройки", + "settingsUpdated": "Настройките са обновени", + "settingsUpdatedDescription": "Всички настройки са успешно обновени", + "settingsErrorUpdate": "Неуспешно обновяване на настройките", + "settingsErrorUpdateDescription": "Възникна грешка при обновяване на настройките", + "sidebarCollapse": "Свиване", + "sidebarExpand": "Разширяване", + "newtUpdateAvailable": "Ново обновление", + "newtUpdateAvailableInfo": "Нова версия на Newt е налична. Моля, обновете до последната версия за най-добро изживяване.", + "domainPickerEnterDomain": "Домейн", "domainPickerPlaceholder": "myapp.example.com", - "domainPickerDescription": "Enter the full domain of the resource to see available options.", - "domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options", - "domainPickerTabAll": "All", - "domainPickerTabOrganization": "Organization", - "domainPickerTabProvided": "Provided", + "domainPickerDescription": "Въведете пълния домейн на ресурса, за да видите наличните опции.", + "domainPickerDescriptionSaas": "Въведете пълен домейн, поддомейн или само име, за да видите наличните опции", + "domainPickerTabAll": "Всички", + "domainPickerTabOrganization": "Организация", + "domainPickerTabProvided": "Предоставен", "domainPickerSortAsc": "A-Z", "domainPickerSortDesc": "Z-A", - "domainPickerCheckingAvailability": "Checking availability...", - "domainPickerNoMatchingDomains": "No matching domains found. Try a different domain or check your organization's domain settings.", - "domainPickerOrganizationDomains": "Organization Domains", - "domainPickerProvidedDomains": "Provided Domains", - "domainPickerSubdomain": "Subdomain: {subdomain}", - "domainPickerNamespace": "Namespace: {namespace}", - "domainPickerShowMore": "Show More", - "domainNotFound": "Domain Not Found", - "domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.", - "failed": "Failed", - "createNewOrgDescription": "Create a new organization", - "organization": "Organization", - "port": "Port", - "securityKeyManage": "Manage Security Keys", - "securityKeyDescription": "Add or remove security keys for passwordless authentication", - "securityKeyRegister": "Register New Security Key", - "securityKeyList": "Your Security Keys", - "securityKeyNone": "No security keys registered yet", - "securityKeyNameRequired": "Name is required", - "securityKeyRemove": "Remove", - "securityKeyLastUsed": "Last used: {date}", - "securityKeyNameLabel": "Security Key Name", - "securityKeyRegisterSuccess": "Security key registered successfully", - "securityKeyRegisterError": "Failed to register security key", - "securityKeyRemoveSuccess": "Security key removed successfully", - "securityKeyRemoveError": "Failed to remove security key", - "securityKeyLoadError": "Failed to load security keys", - "securityKeyLogin": "Continue with security key", - "securityKeyAuthError": "Failed to authenticate with security key", - "securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.", - "registering": "Registering...", - "securityKeyPrompt": "Please verify your identity using your security key. Make sure your security key is connected and ready.", - "securityKeyBrowserNotSupported": "Your browser doesn't support security keys. Please use a modern browser like Chrome, Firefox, or Safari.", - "securityKeyPermissionDenied": "Please allow access to your security key to continue signing in.", - "securityKeyRemovedTooQuickly": "Please keep your security key connected until the sign-in process completes.", - "securityKeyNotSupported": "Your security key may not be compatible. Please try a different security key.", - "securityKeyUnknownError": "There was a problem using your security key. Please try again.", - "twoFactorRequired": "Two-factor authentication is required to register a security key.", - "twoFactor": "Two-Factor Authentication", - "adminEnabled2FaOnYourAccount": "Your administrator has enabled two-factor authentication for {email}. Please complete the setup process to continue.", - "continueToApplication": "Continue to Application", - "securityKeyAdd": "Add Security Key", - "securityKeyRegisterTitle": "Register New Security Key", - "securityKeyRegisterDescription": "Connect your security key and enter a name to identify it", - "securityKeyTwoFactorRequired": "Two-Factor Authentication Required", - "securityKeyTwoFactorDescription": "Please enter your two-factor authentication code to register the security key", - "securityKeyTwoFactorRemoveDescription": "Please enter your two-factor authentication code to remove the security key", - "securityKeyTwoFactorCode": "Two-Factor Code", - "securityKeyRemoveTitle": "Remove Security Key", - "securityKeyRemoveDescription": "Enter your password to remove the security key \"{name}\"", - "securityKeyNoKeysRegistered": "No security keys registered", - "securityKeyNoKeysDescription": "Add a security key to enhance your account security", - "createDomainRequired": "Domain is required", - "createDomainAddDnsRecords": "Add DNS Records", - "createDomainAddDnsRecordsDescription": "Add the following DNS records to your domain provider to complete the setup.", - "createDomainNsRecords": "NS Records", - "createDomainRecord": "Record", - "createDomainType": "Type:", - "createDomainName": "Name:", - "createDomainValue": "Value:", - "createDomainCnameRecords": "CNAME Records", - "createDomainARecords": "A Records", - "createDomainRecordNumber": "Record {number}", - "createDomainTxtRecords": "TXT Records", - "createDomainSaveTheseRecords": "Save These Records", - "createDomainSaveTheseRecordsDescription": "Make sure to save these DNS records as you will not see them again.", - "createDomainDnsPropagation": "DNS Propagation", - "createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.", - "resourcePortRequired": "Port number is required for non-HTTP resources", - "resourcePortNotAllowed": "Port number should not be set for HTTP resources", + "domainPickerCheckingAvailability": "Проверка на наличността...", + "domainPickerNoMatchingDomains": "Не са намерени съвпадащи домейни. Опитайте се с друг домейн или проверете настройките на домейна на вашата организация.", + "domainPickerOrganizationDomains": "Домейни на организацията", + "domainPickerProvidedDomains": "Предоставени домейни", + "domainPickerSubdomain": "Поддомейн: {subdomain}", + "domainPickerNamespace": "Име на пространство: {namespace}", + "domainPickerShowMore": "Покажи повече", + "domainNotFound": "Домейнът не е намерен", + "domainNotFoundDescription": "Този ресурс е деактивиран, защото домейнът вече не съществува в нашата система. Моля, задайте нов домейн за този ресурс.", + "failed": "Неуспешно", + "createNewOrgDescription": "Създайте нова организация", + "organization": "Организация", + "port": "Порт", + "securityKeyManage": "Управление на ключове за защита", + "securityKeyDescription": "Добавяне или премахване на ключове за защита за удостоверяване без парола", + "securityKeyRegister": "Регистриране на нов ключ за защита", + "securityKeyList": "Вашите ключове за защита", + "securityKeyNone": "Все още не са регистрирани ключове за защита", + "securityKeyNameRequired": "Името е задължително", + "securityKeyRemove": "Премахване", + "securityKeyLastUsed": "Последно използван: {date}", + "securityKeyNameLabel": "Име на ключа за сигурност", + "securityKeyRegisterSuccess": "Ключът за защита е регистриран успешно", + "securityKeyRegisterError": "Неуспешна регистрация на ключ за защита", + "securityKeyRemoveSuccess": "Ключът за защита е премахнат успешно", + "securityKeyRemoveError": "Неуспешно премахване на ключ за защита", + "securityKeyLoadError": "Неуспешно зареждане на ключове за защита", + "securityKeyLogin": "Продължете с ключа за сигурност", + "securityKeyAuthError": "Неуспешно удостоверяване с ключ за сигурност", + "securityKeyRecommendation": "Регистрирайте резервен ключ за безопасност на друго устройство, за да сте сигурни, че винаги ще имате достъп до профила си", + "registering": "Регистрация...", + "securityKeyPrompt": "Моля, потвърдете самоличността си, използвайки вашия ключ за защита. Уверете се, че е свързан и готов за употреба", + "securityKeyBrowserNotSupported": "Вашият браузър не поддържа ключове за сигурност. Моля, използвайте модерен браузър като Chrome, Firefox или Safari.", + "securityKeyPermissionDenied": "Моля, позволете достъп до ключа за защита, за да продължите с влизането.", + "securityKeyRemovedTooQuickly": "Моля, дръжте ключа си за сигурност свързан, докато процеса на влизане приключи.", + "securityKeyNotSupported": "Вашият ключ за сигурност може да не е съвместим. Моля, опитайте с друг ключ.", + "securityKeyUnknownError": "Възникна проблем при използването на ключа за сигурност. Моля, опитайте отново.", + "twoFactorRequired": "Двуфакторното удостоверяване е необходимо за регистрация на ключ за защита.", + "twoFactor": "Двуфакторно удостоверяване", + "adminEnabled2FaOnYourAccount": "Вашият администратор е активирал двуфакторно удостоверяване за {email}. Моля, завършете процеса по настройка, за да продължите.", + "continueToApplication": "Продължаване към приложението", + "securityKeyAdd": "Добавяне на ключ за сигурност", + "securityKeyRegisterTitle": "Регистриране на нов ключ за сигурност", + "securityKeyRegisterDescription": "Свържете ключа за сигурност и въведете име, по което да го идентифицирате", + "securityKeyTwoFactorRequired": "Необходимо е двуфакторно удостоверяване", + "securityKeyTwoFactorDescription": "Моля, въведете вашия код за двуфакторно удостоверяване, за да регистрирате ключа за сигурност", + "securityKeyTwoFactorRemoveDescription": "Моля, въведете вашия код за двуфакторно удостоверяване, за да премахнете ключа за сигурност", + "securityKeyTwoFactorCode": "Двуфакторен код", + "securityKeyRemoveTitle": "Премахване на ключ за сигурност", + "securityKeyRemoveDescription": "Въведете паролата си, за да премахнете ключа за сигурност “{name}”", + "securityKeyNoKeysRegistered": "Няма регистрирани ключове за сигурност", + "securityKeyNoKeysDescription": "Добавяне на ключ за сигурност, за да подобрите сигурността на профила си", + "createDomainRequired": "Домейнът е задължителен", + "createDomainAddDnsRecords": "Добавяне на DNS записи", + "createDomainAddDnsRecordsDescription": "Добавете следните DNS записи на вашия домейн провайдер, за да завършите настройката.", + "createDomainNsRecords": "NS записи", + "createDomainRecord": "Запис", + "createDomainType": "Тип:", + "createDomainName": "Име:", + "createDomainValue": "Стойност:", + "createDomainCnameRecords": "CNAME записи", + "createDomainARecords": "A записи", + "createDomainRecordNumber": "Запис {number}", + "createDomainTxtRecords": "TXT записи", + "createDomainSaveTheseRecords": "Запазете тези записи", + "createDomainSaveTheseRecordsDescription": "Уверете се, че запазвате тези DNS записи, тъй като няма да ги видите отново.", + "createDomainDnsPropagation": "Разпространение на DNS", + "createDomainDnsPropagationDescription": "Промените в DNS може да отнемат време, за да се разпространят в интернет. Това може да отнеме от няколко минути до 48 часа, в зависимост от вашия DNS доставчик и TTL настройките .", + "resourcePortRequired": "Номерът на порта е задължителен за не-HTTP ресурси", + "resourcePortNotAllowed": "Номерът на порта не трябва да бъде задаван за HTTP ресурси", "signUpTerms": { - "IAgreeToThe": "I agree to the", - "termsOfService": "terms of service", - "and": "and", - "privacyPolicy": "privacy policy" + "IAgreeToThe": "Съгласен съм с", + "termsOfService": "условията за ползване", + "and": "и", + "privacyPolicy": "политиката за поверителност" }, - "siteRequired": "Site is required.", - "olmTunnel": "Olm Tunnel", - "olmTunnelDescription": "Use Olm for client connectivity", - "errorCreatingClient": "Error creating client", - "clientDefaultsNotFound": "Client defaults not found", - "createClient": "Create Client", - "createClientDescription": "Create a new client for connecting to your sites", - "seeAllClients": "See All Clients", - "clientInformation": "Client Information", - "clientNamePlaceholder": "Client name", - "address": "Address", - "subnetPlaceholder": "Subnet", - "addressDescription": "The address that this client will use for connectivity", - "selectSites": "Select sites", - "sitesDescription": "The client will have connectivity to the selected sites", - "clientInstallOlm": "Install Olm", - "clientInstallOlmDescription": "Get Olm running on your system", - "clientOlmCredentials": "Olm Credentials", - "clientOlmCredentialsDescription": "This is how Olm will authenticate with the server", - "olmEndpoint": "Olm Endpoint", + "siteRequired": "Изисква се сайт.", + "olmTunnel": "Olm тунел", + "olmTunnelDescription": "Използвайте Olm за клиентска свързаност", + "errorCreatingClient": "Възникна грешка при създаване на клиент", + "clientDefaultsNotFound": "Не са намерени настройки по подразбиране за клиента", + "createClient": "Създаване на клиент", + "createClientDescription": "Създайте нов клиент за свързване към вашите сайтове", + "seeAllClients": "Виж всички клиенти", + "clientInformation": "Информация за клиента", + "clientNamePlaceholder": "Име на клиента", + "address": "Адрес", + "subnetPlaceholder": "Мрежа", + "addressDescription": "Адресът, който клиентът ще използва за свързване", + "selectSites": "Избор на сайтове", + "sitesDescription": "Клиентът ще има връзка с избраните сайтове", + "clientInstallOlm": "Инсталиране на Olm", + "clientInstallOlmDescription": "Конфигурирайте Olm да работи на вашата система", + "clientOlmCredentials": "Olm Удостоверения", + "clientOlmCredentialsDescription": "Това е как Olm ще се удостоверява със сървъра", + "olmEndpoint": "Olm Ендпойнт", "olmId": "Olm ID", - "olmSecretKey": "Olm Secret Key", - "clientCredentialsSave": "Save Your Credentials", - "clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "generalSettingsDescription": "Configure the general settings for this client", - "clientUpdated": "Client updated", - "clientUpdatedDescription": "The client has been updated.", - "clientUpdateFailed": "Failed to update client", - "clientUpdateError": "An error occurred while updating the client.", - "sitesFetchFailed": "Failed to fetch sites", - "sitesFetchError": "An error occurred while fetching sites.", - "olmErrorFetchReleases": "An error occurred while fetching Olm releases.", - "olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.", - "remoteSubnets": "Remote Subnets", - "enterCidrRange": "Enter CIDR range", - "remoteSubnetsDescription": "Add CIDR ranges that can be accessed from this site remotely using clients. Use format like 10.0.0.0/24. This ONLY applies to VPN client connectivity.", - "resourceEnableProxy": "Enable Public Proxy", - "resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.", - "externalProxyEnabled": "External Proxy Enabled", - "addNewTarget": "Add New Target", - "targetsList": "Targets List", - "targetErrorDuplicateTargetFound": "Duplicate target found", - "httpMethod": "HTTP Method", - "selectHttpMethod": "Select HTTP method", - "domainPickerSubdomainLabel": "Subdomain", - "domainPickerBaseDomainLabel": "Base Domain", - "domainPickerSearchDomains": "Search domains...", - "domainPickerNoDomainsFound": "No domains found", - "domainPickerLoadingDomains": "Loading domains...", - "domainPickerSelectBaseDomain": "Select base domain...", - "domainPickerNotAvailableForCname": "Not available for CNAME domains", - "domainPickerEnterSubdomainOrLeaveBlank": "Enter subdomain or leave blank to use base domain.", - "domainPickerEnterSubdomainToSearch": "Enter a subdomain to search and select from available free domains.", - "domainPickerFreeDomains": "Free Domains", - "domainPickerSearchForAvailableDomains": "Search for available domains", - "resourceDomain": "Domain", - "resourceEditDomain": "Edit Domain", - "siteName": "Site Name", - "proxyPort": "Port", - "resourcesTableProxyResources": "Proxy Resources", - "resourcesTableClientResources": "Client Resources", - "resourcesTableNoProxyResourcesFound": "No proxy resources found.", - "resourcesTableNoInternalResourcesFound": "No internal resources found.", - "resourcesTableDestination": "Destination", - "resourcesTableTheseResourcesForUseWith": "These resources are for use with", - "resourcesTableClients": "Clients", - "resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.", - "editInternalResourceDialogEditClientResource": "Edit Client Resource", - "editInternalResourceDialogUpdateResourceProperties": "Update the resource properties and target configuration for {resourceName}.", - "editInternalResourceDialogResourceProperties": "Resource Properties", - "editInternalResourceDialogName": "Name", - "editInternalResourceDialogProtocol": "Protocol", - "editInternalResourceDialogSitePort": "Site Port", - "editInternalResourceDialogTargetConfiguration": "Target Configuration", - "editInternalResourceDialogCancel": "Cancel", - "editInternalResourceDialogSaveResource": "Save Resource", - "editInternalResourceDialogSuccess": "Success", - "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Internal resource updated successfully", - "editInternalResourceDialogError": "Error", - "editInternalResourceDialogFailedToUpdateInternalResource": "Failed to update internal resource", - "editInternalResourceDialogNameRequired": "Name is required", - "editInternalResourceDialogNameMaxLength": "Name must be less than 255 characters", - "editInternalResourceDialogProxyPortMin": "Proxy port must be at least 1", - "editInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536", - "editInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format", - "editInternalResourceDialogDestinationPortMin": "Destination port must be at least 1", - "editInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536", - "createInternalResourceDialogNoSitesAvailable": "No Sites Available", - "createInternalResourceDialogNoSitesAvailableDescription": "You need to have at least one Newt site with a subnet configured to create internal resources.", - "createInternalResourceDialogClose": "Close", - "createInternalResourceDialogCreateClientResource": "Create Client Resource", - "createInternalResourceDialogCreateClientResourceDescription": "Create a new resource that will be accessible to clients connected to the selected site.", - "createInternalResourceDialogResourceProperties": "Resource Properties", - "createInternalResourceDialogName": "Name", - "createInternalResourceDialogSite": "Site", - "createInternalResourceDialogSelectSite": "Select site...", - "createInternalResourceDialogSearchSites": "Search sites...", - "createInternalResourceDialogNoSitesFound": "No sites found.", - "createInternalResourceDialogProtocol": "Protocol", + "olmSecretKey": "Olm Тайна парола", + "clientCredentialsSave": "Запазете вашите удостоверения", + "clientCredentialsSaveDescription": "Ще можете да го видите само веднъж. Уверете се, че ще го копирате на сигурно място.", + "generalSettingsDescription": "Конфигурирайте общите настройки за този клиент", + "clientUpdated": "Клиентът актуализиран", + "clientUpdatedDescription": "Клиентът беше актуализиран.", + "clientUpdateFailed": "Актуализацията на клиента неуспешна", + "clientUpdateError": "Възникна грешка по време на актуализацията на клиента.", + "sitesFetchFailed": "Неуспешно получаване на сайтове", + "sitesFetchError": "Възникна грешка при получаването на сайтовете.", + "olmErrorFetchReleases": "Възникна грешка при получаването на Olm версиите.", + "olmErrorFetchLatest": "Възникна грешка при получаването на последната версия на Olm.", + "remoteSubnets": "Отдалечени подмрежи", + "enterCidrRange": "Въведете CIDR обхват", + "remoteSubnetsDescription": "Добавете CIDR диапазони, които могат да бъдат достъпни от този сайт отдалечено с клиенти. Използвайте формат като 10.0.0.0/24. Това се прилага САМО за VPN клиентска свързаност.", + "resourceEnableProxy": "Разрешаване на публичен прокси", + "resourceEnableProxyDescription": "Разрешете публично проксиране на този ресурс. Това позволява достъп до ресурса извън мрежата чрез облак на отворен порт. Изисква конфигурация на Traefik.", + "externalProxyEnabled": "Външен прокси разрешен", + "addNewTarget": "Добави нова цел", + "targetsList": "Списък с цели", + "targetErrorDuplicateTargetFound": "Дублирана цел намерена", + "httpMethod": "HTTP Метод", + "selectHttpMethod": "Изберете HTTP метод", + "domainPickerSubdomainLabel": "Поддомен", + "domainPickerBaseDomainLabel": "Основен домейн", + "domainPickerSearchDomains": "Търсене на домейни...", + "domainPickerNoDomainsFound": "Не са намерени домейни", + "domainPickerLoadingDomains": "Зареждане на домейни...", + "domainPickerSelectBaseDomain": "Изберете основен домейн...", + "domainPickerNotAvailableForCname": "Не е налично за CNAME домейни", + "domainPickerEnterSubdomainOrLeaveBlank": "Въведете поддомен или оставете празно, за да използвате основния домейн.", + "domainPickerEnterSubdomainToSearch": "Въведете поддомен, за да търсите и изберете от наличните свободни домейни.", + "domainPickerFreeDomains": "Безплатни домейни", + "domainPickerSearchForAvailableDomains": "Търсене за налични домейни", + "resourceDomain": "Домейн", + "resourceEditDomain": "Редактиране на домейн", + "siteName": "Име на сайта", + "proxyPort": "Порт", + "resourcesTableProxyResources": "Прокси Ресурси", + "resourcesTableClientResources": "Клиентски ресурси", + "resourcesTableNoProxyResourcesFound": "Не са намерени ресурсни проксита.", + "resourcesTableNoInternalResourcesFound": "Не са намерени вътрешни ресурси.", + "resourcesTableDestination": "Дестинация", + "resourcesTableTheseResourcesForUseWith": "Тези ресурси са за използване с", + "resourcesTableClients": "Клиенти", + "resourcesTableAndOnlyAccessibleInternally": "и са достъпни само вътрешно при свързване с клиент.", + "editInternalResourceDialogEditClientResource": "Редактиране на клиентски ресурс", + "editInternalResourceDialogUpdateResourceProperties": "Актуализирайте свойствата на ресурса и конфигурацията на целите за {resourceName}.", + "editInternalResourceDialogResourceProperties": "Свойствата на ресурса", + "editInternalResourceDialogName": "Име", + "editInternalResourceDialogProtocol": "Протокол", + "editInternalResourceDialogSitePort": "Сайт Порт", + "editInternalResourceDialogTargetConfiguration": "Конфигурация на целите", + "editInternalResourceDialogCancel": "Отмяна", + "editInternalResourceDialogSaveResource": "Запазване на ресурс", + "editInternalResourceDialogSuccess": "Успех", + "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Вътрешният ресурс успешно актуализиран", + "editInternalResourceDialogError": "Грешка", + "editInternalResourceDialogFailedToUpdateInternalResource": "Неуспешно актуализиране на вътрешен ресурс", + "editInternalResourceDialogNameRequired": "Името е задължително", + "editInternalResourceDialogNameMaxLength": "Името трябва да е по-малко от 255 символа", + "editInternalResourceDialogProxyPortMin": "Прокси портът трябва да бъде поне 1", + "editInternalResourceDialogProxyPortMax": "Прокси портът трябва да е по-малък от 65536", + "editInternalResourceDialogInvalidIPAddressFormat": "Невалиден формат на IP адрес", + "editInternalResourceDialogDestinationPortMin": "Дестинационният порт трябва да бъде поне 1", + "editInternalResourceDialogDestinationPortMax": "Дестинационният порт трябва да е по-малък от 65536", + "createInternalResourceDialogNoSitesAvailable": "Няма достъпни сайтове", + "createInternalResourceDialogNoSitesAvailableDescription": "Трябва да имате поне един сайт на Newt с конфигурирана мрежа, за да създадете вътрешни ресурси.", + "createInternalResourceDialogClose": "Затвори", + "createInternalResourceDialogCreateClientResource": "Създаване на клиентски ресурс", + "createInternalResourceDialogCreateClientResourceDescription": "Създайте нов ресурс, който ще бъде достъпен за клиентите свързани със избрания сайт.", + "createInternalResourceDialogResourceProperties": "Свойства на ресурса", + "createInternalResourceDialogName": "Име", + "createInternalResourceDialogSite": "Сайт", + "createInternalResourceDialogSelectSite": "Изберете сайт...", + "createInternalResourceDialogSearchSites": "Търсене на сайтове...", + "createInternalResourceDialogNoSitesFound": "Не са намерени сайтове.", + "createInternalResourceDialogProtocol": "Протокол", "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", - "createInternalResourceDialogSitePort": "Site Port", - "createInternalResourceDialogSitePortDescription": "Use this port to access the resource on the site when connected with a client.", - "createInternalResourceDialogTargetConfiguration": "Target Configuration", - "createInternalResourceDialogDestinationIPDescription": "The IP or hostname address of the resource on the site's network.", - "createInternalResourceDialogDestinationPortDescription": "The port on the destination IP where the resource is accessible.", - "createInternalResourceDialogCancel": "Cancel", - "createInternalResourceDialogCreateResource": "Create Resource", - "createInternalResourceDialogSuccess": "Success", - "createInternalResourceDialogInternalResourceCreatedSuccessfully": "Internal resource created successfully", - "createInternalResourceDialogError": "Error", - "createInternalResourceDialogFailedToCreateInternalResource": "Failed to create internal resource", - "createInternalResourceDialogNameRequired": "Name is required", - "createInternalResourceDialogNameMaxLength": "Name must be less than 255 characters", - "createInternalResourceDialogPleaseSelectSite": "Please select a site", - "createInternalResourceDialogProxyPortMin": "Proxy port must be at least 1", - "createInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536", - "createInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format", - "createInternalResourceDialogDestinationPortMin": "Destination port must be at least 1", - "createInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536", - "siteConfiguration": "Configuration", - "siteAcceptClientConnections": "Accept Client Connections", - "siteAcceptClientConnectionsDescription": "Allow other devices to connect through this Newt instance as a gateway using clients.", - "siteAddress": "Site Address", - "siteAddressDescription": "Specify the IP address of the host for clients to connect to. This is the internal address of the site in the Pangolin network for clients to address. Must fall within the Org subnet.", - "autoLoginExternalIdp": "Auto Login with External IDP", - "autoLoginExternalIdpDescription": "Immediately redirect the user to the external IDP for authentication.", - "selectIdp": "Select IDP", - "selectIdpPlaceholder": "Choose an IDP...", - "selectIdpRequired": "Please select an IDP when auto login is enabled.", - "autoLoginTitle": "Redirecting", - "autoLoginDescription": "Redirecting you to the external identity provider for authentication.", - "autoLoginProcessing": "Preparing authentication...", - "autoLoginRedirecting": "Redirecting to login...", - "autoLoginError": "Auto Login Error", - "autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.", - "autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.", + "createInternalResourceDialogSitePort": "Сайт Порт", + "createInternalResourceDialogSitePortDescription": "Използвайте този порт за достъп до ресурса на сайта при свързване с клиент.", + "createInternalResourceDialogTargetConfiguration": "Конфигурация на целите", + "createInternalResourceDialogDestinationIPDescription": "IP или хостният адрес на ресурса в мрежата на сайта.", + "createInternalResourceDialogDestinationPortDescription": "Портът на дестинационния IP, където ресурсът е достъпен.", + "createInternalResourceDialogCancel": "Отмяна", + "createInternalResourceDialogCreateResource": "Създаване на ресурс", + "createInternalResourceDialogSuccess": "Успех", + "createInternalResourceDialogInternalResourceCreatedSuccessfully": "Вътрешният ресурс създаден успешно", + "createInternalResourceDialogError": "Грешка", + "createInternalResourceDialogFailedToCreateInternalResource": "Неуспешно създаване на вътрешен ресурс", + "createInternalResourceDialogNameRequired": "Името е задължително", + "createInternalResourceDialogNameMaxLength": "Името трябва да е по-малко от 255 символа", + "createInternalResourceDialogPleaseSelectSite": "Моля, изберете сайт", + "createInternalResourceDialogProxyPortMin": "Прокси портът трябва да бъде поне 1", + "createInternalResourceDialogProxyPortMax": "Прокси портът трябва да е по-малък от 65536", + "createInternalResourceDialogInvalidIPAddressFormat": "Невалиден формат на IP адрес", + "createInternalResourceDialogDestinationPortMin": "Дестинационният порт трябва да бъде поне 1", + "createInternalResourceDialogDestinationPortMax": "Дестинационният порт трябва да е по-малък от 65536", + "siteConfiguration": "Конфигурация", + "siteAcceptClientConnections": "Приемане на клиентски връзки", + "siteAcceptClientConnectionsDescription": "Позволете на други устройства да се свързват чрез този Newt инстанция като възел чрез клиенти.", + "siteAddress": "Адрес на сайта", + "siteAddressDescription": "Посочете IP адреса на хоста, към който клиентите ще се свързват. Това е вътрешният адрес на сайта в мрежата на Панголиин за адресиране от клиенти. Трябва да е в рамките на подмрежата на Организацията.", + "autoLoginExternalIdp": "Автоматично влизане с Външен IDP", + "autoLoginExternalIdpDescription": "Незабавно пренасочете потребителя към външния IDP за удостоверяване.", + "selectIdp": "Изберете IDP", + "selectIdpPlaceholder": "Изберете IDP...", + "selectIdpRequired": "Моля, изберете IDP, когато автоматичното влизане е разрешено.", + "autoLoginTitle": "Пренасочване", + "autoLoginDescription": "Пренасочване към външния доставчик на идентификационни данни за удостоверяване.", + "autoLoginProcessing": "Подготовка за удостоверяване...", + "autoLoginRedirecting": "Пренасочване към вход...", + "autoLoginError": "Грешка при автоматично влизане", + "autoLoginErrorNoRedirectUrl": "Не е получен URL за пренасочване от доставчика на идентификационни данни.", + "autoLoginErrorGeneratingUrl": "Неуспешно генериране на URL за удостоверяване.", "managedSelfHosted": { - "title": "Managed Self-Hosted", - "description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles", - "introTitle": "Managed Self-Hosted Pangolin", - "introDescription": "is a deployment option designed for people who want simplicity and extra reliability while still keeping their data private and self-hosted.", - "introDetail": "With this option, you still run your own Pangolin node — your tunnels, SSL termination, and traffic all stay on your server. The difference is that management and monitoring are handled through our cloud dashboard, which unlocks a number of benefits:", + "title": "Управлявано Самостоятелно-хоствано", + "description": "По-надежден и по-нисък поддръжка на Самостоятелно-хостван Панголиин сървър с допълнителни екстри", + "introTitle": "Управлявано Самостоятелно-хостван Панголиин", + "introDescription": "е опция за внедряване, предназначена за хора, които искат простота и допълнителна надеждност, като същевременно запазят данните си частни и самостоятелно-хоствани.", + "introDetail": "С тази опция все още управлявате свой собствен Панголиин възел — вашите тунели, SSL терминатора и трафик остават на вашия сървър. Разликата е, че управлението и мониторингът се обработват чрез нашия облачен панел за контрол, който отключва редица предимства:", "benefitSimplerOperations": { - "title": "Simpler operations", - "description": "No need to run your own mail server or set up complex alerting. You'll get health checks and downtime alerts out of the box." + "title": "По-прости операции", + "description": "Няма нужда да управлявате свой собствен имейл сървър или да настройвате сложни аларми. Ще получите проверки и предупреждения при прекъсване от самото начало." }, "benefitAutomaticUpdates": { - "title": "Automatic updates", - "description": "The cloud dashboard evolves quickly, so you get new features and bug fixes without having to manually pull new containers every time." + "title": "Автоматични актуализации", + "description": "Облачният панел за контрол се развива бързо, така че получавате нови функции и корекции на грешки без да се налага да извличате нови контейнери всеки път." }, "benefitLessMaintenance": { - "title": "Less maintenance", - "description": "No database migrations, backups, or extra infrastructure to manage. We handle that in the cloud." + "title": "По-малко поддръжка", + "description": "Няма миграции на база от данни, резервни копия или допълнителна инфраструктура за управление. Ние се грижим за това в облака." }, "benefitCloudFailover": { - "title": "Cloud failover", - "description": "If your node goes down, your tunnels can temporarily fail over to our cloud points of presence until you bring it back online." + "title": "Облачно преобръщане", + "description": "Ако вашият възел спре да работи, вашите тунели могат временно да преориентират към нашите облачни точки, докато не го възстановите." }, "benefitHighAvailability": { - "title": "High availability (PoPs)", - "description": "You can also attach multiple nodes to your account for redundancy and better performance." + "title": "Висока наличност (PoPs)", + "description": "Можете също така да прикрепите множество възли към вашия акаунт за резервно копиране и по-добра производителност." }, "benefitFutureEnhancements": { - "title": "Future enhancements", - "description": "We're planning to add more analytics, alerting, and management tools to make your deployment even more robust." + "title": "Бъдещи подобрения", + "description": "Планираме да добавим още аналитични, алармиращи и управителни инструменти, за да направим вашето внедряване още по-здраво." }, "docsAlert": { - "text": "Learn more about the Managed Self-Hosted option in our", - "documentation": "documentation" + "text": "Научете повече за Управляваното Самостоятелно-хоствано опцията в нашата", + "documentation": "документация" }, - "convertButton": "Convert This Node to Managed Self-Hosted" + "convertButton": "Конвертирайте този възел в Управлявано Самостоятелно-хоствано" }, - "internationaldomaindetected": "International Domain Detected", - "willbestoredas": "Will be stored as:", - "idpGoogleDescription": "Google OAuth2/OIDC provider", - "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Custom Headers", - "headersValidationError": "Headers must be in the format: Header-Name: value.", - "domainPickerProvidedDomain": "Provided Domain", - "domainPickerFreeProvidedDomain": "Free Provided Domain", - "domainPickerVerified": "Verified", - "domainPickerUnverified": "Unverified", - "domainPickerInvalidSubdomainStructure": "This subdomain contains invalid characters or structure. It will be sanitized automatically when you save.", - "domainPickerError": "Error", - "domainPickerErrorLoadDomains": "Failed to load organization domains", - "domainPickerErrorCheckAvailability": "Failed to check domain availability", - "domainPickerInvalidSubdomain": "Invalid subdomain", - "domainPickerInvalidSubdomainRemoved": "The input \"{sub}\" was removed because it's not valid.", - "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" could not be made valid for {domain}.", - "domainPickerSubdomainSanitized": "Subdomain sanitized", - "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", - "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edit file: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "internationaldomaindetected": "Открит международен домейн", + "willbestoredas": "Ще бъде съхранено като:", + "idpGoogleDescription": "Google OAuth2/OIDC доставчик", + "idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик", + "customHeaders": "Персонализирани заглавия", + "headersValidationError": "Заглавията трябва да бъдат във формат: Име на заглавието: стойност.", + "domainPickerProvidedDomain": "Предоставен домейн", + "domainPickerFreeProvidedDomain": "Безплатен предоставен домейн", + "domainPickerVerified": "Проверено", + "domainPickerUnverified": "Непроверено", + "domainPickerInvalidSubdomainStructure": "Този поддомен съдържа невалидни знаци или структура. Ще бъде автоматично пречистен при запазване.", + "domainPickerError": "Грешка", + "domainPickerErrorLoadDomains": "Неуспешно зареждане на домейни на организацията", + "domainPickerErrorCheckAvailability": "Неуспешна проверка на наличността на домейни", + "domainPickerInvalidSubdomain": "Невалиден поддомен", + "domainPickerInvalidSubdomainRemoved": "Входът \"{sub}\" беше премахнат, защото не е валиден.", + "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не може да се направи валиден за {domain}.", + "domainPickerSubdomainSanitized": "Поддомен пречистен", + "domainPickerSubdomainCorrected": "\"{sub}\" беше коригиран на \"{sanitized}\"", + "resourceAddEntrypointsEditFile": "Редактиране на файл: config/traefik/traefik_config.yml", + "resourceExposePortsEditFile": "Редактиране на файл: docker-compose.yml", + "emailVerificationRequired": "Потвърждението на Email е необходимо. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", + "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук." } From b2669aaa3490decc736332dc8e3a1375f3bfb8f3 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:52 -0700 Subject: [PATCH 029/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 2562 +++++++++++++++++++++---------------------- 1 file changed, 1281 insertions(+), 1281 deletions(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 22a64460..424d0663 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -157,9 +157,9 @@ "resourceMessageConfirm": "Pro potvrzení zadejte prosím název zdroje.", "resourceQuestionRemove": "Opravdu chcete odstranit zdroj {selectedResource} z organizace?", "resourceHTTP": "Zdroj HTTPS", - "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", - "resourceRaw": "Raw TCP/UDP Resource", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", + "resourceHTTPDescription": "Požadavky na proxy pro vaši aplikaci přes HTTPS pomocí subdomény nebo základní domény.", + "resourceRaw": "Surový TCP/UDP zdroj", + "resourceRawDescription": "Požadavky na proxy pro vaši aplikaci přes TCP/UDP pomocí čísla portu.", "resourceCreate": "Vytvořit zdroj", "resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj", "resourceSeeAll": "Zobrazit všechny zdroje", @@ -188,1338 +188,1338 @@ "resourceConfigDescription": "Zkopírujte a vložte tyto konfigurační snippety pro nastavení TCP/UDP zdroje", "resourceAddEntrypoints": "Traefik: Přidat vstupní body", "resourceExposePorts": "Gerbil: Expose Ports in Docker Compose", - "resourceLearnRaw": "Learn how to configure TCP/UDP resources", - "resourceBack": "Back to Resources", - "resourceGoTo": "Go to Resource", - "resourceDelete": "Delete Resource", - "resourceDeleteConfirm": "Confirm Delete Resource", - "visibility": "Visibility", - "enabled": "Enabled", - "disabled": "Disabled", - "general": "General", - "generalSettings": "General Settings", - "proxy": "Proxy", - "internal": "Internal", - "rules": "Rules", - "resourceSettingDescription": "Configure the settings on your resource", - "resourceSetting": "{resourceName} Settings", - "alwaysAllow": "Always Allow", - "alwaysDeny": "Always Deny", - "passToAuth": "Pass to Auth", - "orgSettingsDescription": "Configure your organization's general settings", - "orgGeneralSettings": "Organization Settings", - "orgGeneralSettingsDescription": "Manage your organization details and configuration", - "saveGeneralSettings": "Save General Settings", - "saveSettings": "Save Settings", - "orgDangerZone": "Danger Zone", - "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", - "orgDelete": "Delete Organization", - "orgDeleteConfirm": "Confirm Delete Organization", - "orgMessageRemove": "This action is irreversible and will delete all associated data.", - "orgMessageConfirm": "To confirm, please type the name of the organization below.", - "orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?", - "orgUpdated": "Organization updated", - "orgUpdatedDescription": "The organization has been updated.", - "orgErrorUpdate": "Failed to update organization", - "orgErrorUpdateMessage": "An error occurred while updating the organization.", - "orgErrorFetch": "Failed to fetch organizations", - "orgErrorFetchMessage": "An error occurred while listing your organizations", - "orgErrorDelete": "Failed to delete organization", - "orgErrorDeleteMessage": "An error occurred while deleting the organization.", - "orgDeleted": "Organization deleted", - "orgDeletedMessage": "The organization and its data has been deleted.", - "orgMissing": "Organization ID Missing", - "orgMissingMessage": "Unable to regenerate invitation without an organization ID.", - "accessUsersManage": "Manage Users", - "accessUsersDescription": "Invite users and add them to roles to manage access to your organization", - "accessUsersSearch": "Search users...", - "accessUserCreate": "Create User", - "accessUserRemove": "Remove User", - "username": "Username", - "identityProvider": "Identity Provider", + "resourceLearnRaw": "Naučte se konfigurovat zdroje TCP/UDP", + "resourceBack": "Zpět na zdroje", + "resourceGoTo": "Přejít na dokument", + "resourceDelete": "Odstranit dokument", + "resourceDeleteConfirm": "Potvrdit odstranění dokumentu", + "visibility": "Viditelnost", + "enabled": "Povoleno", + "disabled": "Zakázáno", + "general": "Obecná ustanovení", + "generalSettings": "Obecná nastavení", + "proxy": "Proxy server", + "internal": "Interní", + "rules": "Pravidla", + "resourceSettingDescription": "Konfigurace nastavení na vašem zdroji", + "resourceSetting": "Nastavení {resourceName}", + "alwaysAllow": "Vždy povolit", + "alwaysDeny": "Vždy zakázat", + "passToAuth": "Předat k ověření", + "orgSettingsDescription": "Konfigurace obecných nastavení vaší organizace", + "orgGeneralSettings": "Nastavení organizace", + "orgGeneralSettingsDescription": "Spravujte údaje a konfiguraci vaší organizace", + "saveGeneralSettings": "Uložit obecné nastavení", + "saveSettings": "Uložit nastavení", + "orgDangerZone": "Nebezpečná zóna", + "orgDangerZoneDescription": "Jakmile smažete tento org, nic se nevrátí. Buďte si jistí.", + "orgDelete": "Odstranit organizaci", + "orgDeleteConfirm": "Potvrdit odstranění organizace", + "orgMessageRemove": "Tato akce je nevratná a odstraní všechna související data.", + "orgMessageConfirm": "Pro potvrzení zadejte níže uvedený název organizace.", + "orgQuestionRemove": "Opravdu chcete odstranit organizaci {selectedOrg}?", + "orgUpdated": "Organizace byla aktualizována", + "orgUpdatedDescription": "Organizace byla aktualizována.", + "orgErrorUpdate": "Aktualizace organizace se nezdařila", + "orgErrorUpdateMessage": "Došlo k chybě při aktualizaci organizace.", + "orgErrorFetch": "Nepodařilo se načíst organizace", + "orgErrorFetchMessage": "Došlo k chybě při výpisu vašich organizací", + "orgErrorDelete": "Nepodařilo se odstranit organizaci", + "orgErrorDeleteMessage": "Došlo k chybě při odstraňování organizace.", + "orgDeleted": "Organizace odstraněna", + "orgDeletedMessage": "Organizace a její data byla smazána.", + "orgMissing": "Chybí ID organizace", + "orgMissingMessage": "Nelze obnovit pozvánku bez ID organizace.", + "accessUsersManage": "Spravovat uživatele", + "accessUsersDescription": "Pozvěte uživatele a přidejte je do rolí pro správu přístupu do vaší organizace", + "accessUsersSearch": "Hledat uživatele...", + "accessUserCreate": "Vytvořit uživatele", + "accessUserRemove": "Odstranit uživatele", + "username": "Uživatelské jméno", + "identityProvider": "Poskytovatel identity", "role": "Role", - "nameRequired": "Name is required", - "accessRolesManage": "Manage Roles", - "accessRolesDescription": "Configure roles to manage access to your organization", - "accessRolesSearch": "Search roles...", - "accessRolesAdd": "Add Role", - "accessRoleDelete": "Delete Role", - "description": "Description", - "inviteTitle": "Open Invitations", - "inviteDescription": "Manage your invitations to other users", - "inviteSearch": "Search invitations...", - "minutes": "Minutes", - "hours": "Hours", - "days": "Days", - "weeks": "Weeks", - "months": "Months", - "years": "Years", - "day": "{count, plural, one {# day} other {# days}}", - "apiKeysTitle": "API Key Information", - "apiKeysConfirmCopy2": "You must confirm that you have copied the API key.", - "apiKeysErrorCreate": "Error creating API key", - "apiKeysErrorSetPermission": "Error setting permissions", - "apiKeysCreate": "Generate API Key", - "apiKeysCreateDescription": "Generate a new API key for your organization", - "apiKeysGeneralSettings": "Permissions", - "apiKeysGeneralSettingsDescription": "Determine what this API key can do", - "apiKeysList": "Your API Key", - "apiKeysSave": "Save Your API Key", - "apiKeysSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "apiKeysInfo": "Your API key is:", - "apiKeysConfirmCopy": "I have copied the API key", - "generate": "Generate", - "done": "Done", - "apiKeysSeeAll": "See All API Keys", - "apiKeysPermissionsErrorLoadingActions": "Error loading API key actions", - "apiKeysPermissionsErrorUpdate": "Error setting permissions", - "apiKeysPermissionsUpdated": "Permissions updated", - "apiKeysPermissionsUpdatedDescription": "The permissions have been updated.", - "apiKeysPermissionsGeneralSettings": "Permissions", - "apiKeysPermissionsGeneralSettingsDescription": "Determine what this API key can do", - "apiKeysPermissionsSave": "Save Permissions", - "apiKeysPermissionsTitle": "Permissions", - "apiKeys": "API Keys", - "searchApiKeys": "Search API keys...", - "apiKeysAdd": "Generate API Key", - "apiKeysErrorDelete": "Error deleting API key", - "apiKeysErrorDeleteMessage": "Error deleting API key", - "apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?", - "apiKeysMessageRemove": "Once removed, the API key will no longer be able to be used.", - "apiKeysMessageConfirm": "To confirm, please type the name of the API key below.", - "apiKeysDeleteConfirm": "Confirm Delete API Key", - "apiKeysDelete": "Delete API Key", - "apiKeysManage": "Manage API Keys", - "apiKeysDescription": "API keys are used to authenticate with the integration API", - "apiKeysSettings": "{apiKeyName} Settings", - "userTitle": "Manage All Users", - "userDescription": "View and manage all users in the system", - "userAbount": "About User Management", - "userAbountDescription": "This table displays all root user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their root user object - they will remain in the system. To completely remove a user from the system, you must delete their root user object using the delete action in this table.", - "userServer": "Server Users", - "userSearch": "Search server users...", - "userErrorDelete": "Error deleting user", - "userDeleteConfirm": "Confirm Delete User", - "userDeleteServer": "Delete User from Server", - "userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.", - "userMessageConfirm": "To confirm, please type the name of the user below.", - "userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?", - "licenseKey": "License Key", + "nameRequired": "Název je povinný", + "accessRolesManage": "Spravovat role", + "accessRolesDescription": "Konfigurace rolí pro správu přístupu do vaší organizace", + "accessRolesSearch": "Hledat role...", + "accessRolesAdd": "Přidat roli", + "accessRoleDelete": "Odstranit roli", + "description": "L 343, 22.12.2009, s. 1).", + "inviteTitle": "Otevřít pozvánky", + "inviteDescription": "Spravujte své pozvánky ostatním uživatelům", + "inviteSearch": "Hledat pozvánky...", + "minutes": "Zápis z jednání", + "hours": "Hodiny", + "days": "Dny", + "weeks": "Týdny", + "months": "Měsíce", + "years": "Roky", + "day": "{count, plural, one {# den} other {# dní}}", + "apiKeysTitle": "Informace API klíče", + "apiKeysConfirmCopy2": "Musíte potvrdit, že jste zkopírovali API klíč.", + "apiKeysErrorCreate": "Chyba při vytváření API klíče", + "apiKeysErrorSetPermission": "Chyba nastavení oprávnění", + "apiKeysCreate": "Generovat API klíč", + "apiKeysCreateDescription": "Vygenerovat nový API klíč pro vaši organizaci", + "apiKeysGeneralSettings": "Práva", + "apiKeysGeneralSettingsDescription": "Určete, co může tento API klíč udělat", + "apiKeysList": "Váš API klíč", + "apiKeysSave": "Uložit váš API klíč", + "apiKeysSaveDescription": "Toto nastavení uvidíte pouze jednou. Ujistěte se, že jej zkopírujete na bezpečné místo.", + "apiKeysInfo": "Váš API klíč je:", + "apiKeysConfirmCopy": "Kopíroval jsem API klíč", + "generate": "Generovat", + "done": "Hotovo", + "apiKeysSeeAll": "Zobrazit všechny API klíče", + "apiKeysPermissionsErrorLoadingActions": "Chyba při načítání akcí API klíče", + "apiKeysPermissionsErrorUpdate": "Chyba nastavení oprávnění", + "apiKeysPermissionsUpdated": "Oprávnění byla aktualizována", + "apiKeysPermissionsUpdatedDescription": "Oprávnění byla aktualizována.", + "apiKeysPermissionsGeneralSettings": "Práva", + "apiKeysPermissionsGeneralSettingsDescription": "Určete, co může tento API klíč udělat", + "apiKeysPermissionsSave": "Uložit oprávnění", + "apiKeysPermissionsTitle": "Práva", + "apiKeys": "API klíče", + "searchApiKeys": "Hledat API klíče...", + "apiKeysAdd": "Generovat API klíč", + "apiKeysErrorDelete": "Chyba při odstraňování API klíče", + "apiKeysErrorDeleteMessage": "Chyba při odstraňování API klíče", + "apiKeysQuestionRemove": "Opravdu chcete odstranit API klíč {selectedApiKey} z organizace?", + "apiKeysMessageRemove": "Po odstranění klíče API již nebude možné použít.", + "apiKeysMessageConfirm": "Pro potvrzení zadejte název klíče API.", + "apiKeysDeleteConfirm": "Potvrdit odstranění API klíče", + "apiKeysDelete": "Odstranit klíč API", + "apiKeysManage": "Správa API klíčů", + "apiKeysDescription": "API klíče se používají k ověření s integračním API", + "apiKeysSettings": "Nastavení {apiKeyName}", + "userTitle": "Spravovat všechny uživatele", + "userDescription": "Zobrazit a spravovat všechny uživatele v systému", + "userAbount": "O správě uživatelů", + "userAbountDescription": "Tato tabulka zobrazuje všechny root uživatelské objekty v systému. Každý uživatel může patřit do více organizací. Odstranění uživatele z organizace neodstraní jeho kořenový uživatelský objekt - zůstanou v systému. Pro úplné odstranění uživatele ze systému musíte odstranit jejich kořenový uživatelský objekt pomocí smazané akce v této tabulce.", + "userServer": "Uživatelé serveru", + "userSearch": "Hledat uživatele serveru...", + "userErrorDelete": "Chyba při odstraňování uživatele", + "userDeleteConfirm": "Potvrdit odstranění uživatele", + "userDeleteServer": "Odstranit uživatele ze serveru", + "userMessageRemove": "Uživatel bude odstraněn ze všech organizací a bude zcela odstraněn ze serveru.", + "userMessageConfirm": "Pro potvrzení zadejte níže uvedené jméno uživatele.", + "userQuestionRemove": "Opravdu chcete trvale odstranit {selectedUser} ze serveru?", + "licenseKey": "Licenční klíč", "valid": "Valid", - "numberOfSites": "Number of Sites", - "licenseKeySearch": "Search license keys...", - "licenseKeyAdd": "Add License Key", - "type": "Type", - "licenseKeyRequired": "License key is required", - "licenseTermsAgree": "You must agree to the license terms", - "licenseErrorKeyLoad": "Failed to load license keys", - "licenseErrorKeyLoadDescription": "An error occurred loading license keys.", - "licenseErrorKeyDelete": "Failed to delete license key", - "licenseErrorKeyDeleteDescription": "An error occurred deleting license key.", - "licenseKeyDeleted": "License key deleted", - "licenseKeyDeletedDescription": "The license key has been deleted.", - "licenseErrorKeyActivate": "Failed to activate license key", - "licenseErrorKeyActivateDescription": "An error occurred while activating the license key.", - "licenseAbout": "About Licensing", - "communityEdition": "Community Edition", - "licenseAboutDescription": "This is for business and enterprise users who are using Pangolin in a commercial environment. If you are using Pangolin for personal use, you can ignore this section.", - "licenseKeyActivated": "License key activated", - "licenseKeyActivatedDescription": "The license key has been successfully activated.", - "licenseErrorKeyRecheck": "Failed to recheck license keys", - "licenseErrorKeyRecheckDescription": "An error occurred rechecking license keys.", - "licenseErrorKeyRechecked": "License keys rechecked", - "licenseErrorKeyRecheckedDescription": "All license keys have been rechecked", - "licenseActivateKey": "Activate License Key", - "licenseActivateKeyDescription": "Enter a license key to activate it.", - "licenseActivate": "Activate License", - "licenseAgreement": "By checking this box, you confirm that you have read and agree to the license terms corresponding to the tier associated with your license key.", - "fossorialLicense": "View Fossorial Commercial License & Subscription Terms", - "licenseMessageRemove": "This will remove the license key and all associated permissions granted by it.", - "licenseMessageConfirm": "To confirm, please type the license key below.", - "licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?", - "licenseKeyDelete": "Delete License Key", - "licenseKeyDeleteConfirm": "Confirm Delete License Key", - "licenseTitle": "Manage License Status", - "licenseTitleDescription": "View and manage license keys in the system", - "licenseHost": "Host License", - "licenseHostDescription": "Manage the main license key for the host.", - "licensedNot": "Not Licensed", - "hostId": "Host ID", - "licenseReckeckAll": "Recheck All Keys", - "licenseSiteUsage": "Sites Usage", - "licenseSiteUsageDecsription": "View the number of sites using this license.", - "licenseNoSiteLimit": "There is no limit on the number of sites using an unlicensed host.", - "licensePurchase": "Purchase License", - "licensePurchaseSites": "Purchase Additional Sites", - "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", - "licenseSitesUsed": "{count, plural, =0 {# sites} one {# site} other {# sites}} in system.", - "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", - "licenseFee": "License fee", - "licensePriceSite": "Price per site", - "total": "Total", - "licenseContinuePayment": "Continue to Payment", - "pricingPage": "pricing page", - "pricingPortal": "See Purchase Portal", - "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", - "inviteRemove": "Remove Invitation", - "inviteRemoveError": "Failed to remove invitation", - "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", - "inviteRemoved": "Invitation removed", - "inviteRemovedDescription": "The invitation for {email} has been removed.", - "inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?", - "inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.", - "inviteMessageConfirm": "To confirm, please type the email address of the invitation below.", - "inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.", - "inviteRemoveConfirm": "Confirm Remove Invitation", - "inviteRegenerated": "Invitation Regenerated", - "inviteSent": "A new invitation has been sent to {email}.", - "inviteSentEmail": "Send email notification to the user", - "inviteGenerate": "A new invitation has been generated for {email}.", + "numberOfSites": "Počet stránek", + "licenseKeySearch": "Hledat licenční klíče...", + "licenseKeyAdd": "Přidat licenční klíč", + "type": "Typ", + "licenseKeyRequired": "Je vyžadován licenční klíč", + "licenseTermsAgree": "Musíte souhlasit s podmínkami licence", + "licenseErrorKeyLoad": "Nepodařilo se načíst licenční klíče", + "licenseErrorKeyLoadDescription": "Došlo k chybě při načítání licenčních klíčů.", + "licenseErrorKeyDelete": "Nepodařilo se odstranit licenční klíč", + "licenseErrorKeyDeleteDescription": "Došlo k chybě při odstraňování licenčního klíče.", + "licenseKeyDeleted": "Licenční klíč byl smazán", + "licenseKeyDeletedDescription": "Licenční klíč byl odstraněn.", + "licenseErrorKeyActivate": "Nepodařilo se aktivovat licenční klíč", + "licenseErrorKeyActivateDescription": "Došlo k chybě při aktivaci licenčního klíče.", + "licenseAbout": "O licencích", + "communityEdition": "Komunitní edice", + "licenseAboutDescription": "To je pro obchodní a podnikové uživatele, kteří používají Pangolin v komerčním prostředí. Pokud používáte Pangolin pro osobní použití, můžete tuto sekci ignorovat.", + "licenseKeyActivated": "Licenční klíč aktivován", + "licenseKeyActivatedDescription": "Licenční klíč byl úspěšně aktivován.", + "licenseErrorKeyRecheck": "Nepodařilo se znovu zkontrolovat licenční klíče", + "licenseErrorKeyRecheckDescription": "Došlo k chybě při opětovné kontrole licenčních klíčů.", + "licenseErrorKeyRechecked": "Licenční klíče překontrolovány", + "licenseErrorKeyRecheckedDescription": "Všechny licenční klíče byly znovu zkontrolovány", + "licenseActivateKey": "Aktivovat licenční klíč", + "licenseActivateKeyDescription": "Zadejte licenční klíč pro jeho aktivaci.", + "licenseActivate": "Aktivovat licenci", + "licenseAgreement": "Zaškrtnutím tohoto políčka potvrdíte, že jste si přečetli licenční podmínky odpovídající úrovni přiřazené k vašemu licenčnímu klíči a souhlasíte s nimi.", + "fossorialLicense": "Zobrazit Fossorial Commercial License & Subscription terms", + "licenseMessageRemove": "Tímto odstraníte licenční klíč a všechna s ním spojená oprávnění, která mu byla udělena.", + "licenseMessageConfirm": "Pro potvrzení zadejte licenční klíč níže.", + "licenseQuestionRemove": "Jste si jisti, že chcete odstranit licenční klíč {selectedKey}?", + "licenseKeyDelete": "Odstranit licenční klíč", + "licenseKeyDeleteConfirm": "Potvrdit odstranění licenčního klíče", + "licenseTitle": "Správa stavu licence", + "licenseTitleDescription": "Zobrazit a spravovat licenční klíče v systému", + "licenseHost": "Licence hostitele", + "licenseHostDescription": "Správa hlavního licenčního klíče pro hostitele.", + "licensedNot": "Bez licence", + "hostId": "ID hostitele", + "licenseReckeckAll": "Znovu zobrazit všechny klíče", + "licenseSiteUsage": "Využití stránek", + "licenseSiteUsageDecsription": "Zobrazit počet stránek používajících tuto licenci.", + "licenseNoSiteLimit": "Neexistuje žádný limit počtu webů používajících nelicencovaný hostitele.", + "licensePurchase": "Zakoupit licenci", + "licensePurchaseSites": "Zakoupit další stránky", + "licenseSitesUsedMax": "{usedSites} použitých stránek {maxSites}", + "licenseSitesUsed": "{count, plural, =0 {# stránek} one {# stránky} other {# stránek}}", + "licensePurchaseDescription": "Vyberte kolik stránek chcete {selectedMode, select, license {Zakupte si licenci. Vždy můžete přidat více webů později.} other {Přidejte k vaší existující licenci.}}", + "licenseFee": "Licenční poplatek", + "licensePriceSite": "Cena za stránku", + "total": "Celkem", + "licenseContinuePayment": "Pokračovat v platbě", + "pricingPage": "cenová stránka", + "pricingPortal": "Zobrazit nákupní portál", + "licensePricingPage": "Pro nejaktuálnější ceny a slevy navštivte ", + "invite": "Pozvánky", + "inviteRegenerate": "Obnovit pozvánku", + "inviteRegenerateDescription": "Zrušit předchozí pozvání a vytvořit nové", + "inviteRemove": "Odstranit pozvánku", + "inviteRemoveError": "Nepodařilo se odstranit pozvánku", + "inviteRemoveErrorDescription": "Došlo k chybě při odstraňování pozvánky.", + "inviteRemoved": "Pozvánka odstraněna", + "inviteRemovedDescription": "Pozvánka pro {email} byla odstraněna.", + "inviteQuestionRemove": "Jste si jisti, že chcete odstranit pozvánku {email}?", + "inviteMessageRemove": "Po odstranění, tato pozvánka již nebude platná. Později můžete uživatele znovu pozvat.", + "inviteMessageConfirm": "Pro potvrzení zadejte prosím níže uvedenou e-mailovou adresu.", + "inviteQuestionRegenerate": "Jste si jisti, že chcete obnovit pozvánku pro {email}? Tato akce zruší předchozí pozvánku.", + "inviteRemoveConfirm": "Potvrdit odstranění pozvánky", + "inviteRegenerated": "Pozvánka obnovena", + "inviteSent": "Nová pozvánka byla odeslána na {email}.", + "inviteSentEmail": "Poslat uživateli oznámení e-mailem", + "inviteGenerate": "Nová pozvánka byla vygenerována pro {email}.", "inviteDuplicateError": "Duplicate Invite", - "inviteDuplicateErrorDescription": "An invitation for this user already exists.", - "inviteRateLimitError": "Rate Limit Exceeded", - "inviteRateLimitErrorDescription": "You have exceeded the limit of 3 regenerations per hour. Please try again later.", - "inviteRegenerateError": "Failed to Regenerate Invitation", - "inviteRegenerateErrorDescription": "An error occurred while regenerating the invitation.", - "inviteValidityPeriod": "Validity Period", - "inviteValidityPeriodSelect": "Select validity period", - "inviteRegenerateMessage": "The invitation has been regenerated. The user must access the link below to accept the invitation.", - "inviteRegenerateButton": "Regenerate", - "expiresAt": "Expires At", - "accessRoleUnknown": "Unknown Role", - "placeholder": "Placeholder", - "userErrorOrgRemove": "Failed to remove user", - "userErrorOrgRemoveDescription": "An error occurred while removing the user.", - "userOrgRemoved": "User removed", - "userOrgRemovedDescription": "The user {email} has been removed from the organization.", - "userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?", - "userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.", - "userMessageOrgConfirm": "To confirm, please type the name of the of the user below.", - "userRemoveOrgConfirm": "Confirm Remove User", - "userRemoveOrg": "Remove User from Organization", - "users": "Users", - "accessRoleMember": "Member", - "accessRoleOwner": "Owner", - "userConfirmed": "Confirmed", - "idpNameInternal": "Internal", - "emailInvalid": "Invalid email address", - "inviteValidityDuration": "Please select a duration", - "accessRoleSelectPlease": "Please select a role", - "usernameRequired": "Username is required", - "idpSelectPlease": "Please select an identity provider", + "inviteDuplicateErrorDescription": "Pozvánka pro tohoto uživatele již existuje.", + "inviteRateLimitError": "Limit sazby překročen", + "inviteRateLimitErrorDescription": "Překročil jsi limit 3 regenerací za hodinu. Opakujte akci později.", + "inviteRegenerateError": "Nepodařilo se obnovit pozvánku", + "inviteRegenerateErrorDescription": "Došlo k chybě při obnovování pozvánky.", + "inviteValidityPeriod": "Doba platnosti", + "inviteValidityPeriodSelect": "Vyberte dobu platnosti", + "inviteRegenerateMessage": "Pozvánka byla obnovena. Uživatel musí mít přístup k níže uvedenému odkazu, aby mohl pozvánku přijmout.", + "inviteRegenerateButton": "Regenerovat", + "expiresAt": "Vyprší v", + "accessRoleUnknown": "Neznámá role", + "placeholder": "Zástupný symbol", + "userErrorOrgRemove": "Odstranění uživatele se nezdařilo", + "userErrorOrgRemoveDescription": "Došlo k chybě při odebírání uživatele.", + "userOrgRemoved": "Uživatel odstraněn", + "userOrgRemovedDescription": "Uživatel {email} byl odebrán z organizace.", + "userQuestionOrgRemove": "Jste si jisti, že chcete odstranit {email} z organizace?", + "userMessageOrgRemove": "Po odstranění tohoto uživatele již nebude mít přístup k organizaci. Vždy je můžete znovu pozvat později, ale budou muset pozvání znovu přijmout.", + "userMessageOrgConfirm": "Pro potvrzení, zadejte prosím jméno uživatele níže.", + "userRemoveOrgConfirm": "Potvrdit odebrání uživatele", + "userRemoveOrg": "Odebrat uživatele z organizace", + "users": "Uživatelé", + "accessRoleMember": "Člen", + "accessRoleOwner": "Vlastník", + "userConfirmed": "Potvrzeno", + "idpNameInternal": "Interní", + "emailInvalid": "Neplatná e-mailová adresa", + "inviteValidityDuration": "Zvolte prosím dobu trvání", + "accessRoleSelectPlease": "Vyberte prosím roli", + "usernameRequired": "Uživatelské jméno je povinné", + "idpSelectPlease": "Vyberte poskytovatele identity", "idpGenericOidc": "Generic OAuth2/OIDC provider.", - "accessRoleErrorFetch": "Failed to fetch roles", - "accessRoleErrorFetchDescription": "An error occurred while fetching the roles", - "idpErrorFetch": "Failed to fetch identity providers", - "idpErrorFetchDescription": "An error occurred while fetching identity providers", - "userErrorExists": "User Already Exists", - "userErrorExistsDescription": "This user is already a member of the organization.", - "inviteError": "Failed to invite user", - "inviteErrorDescription": "An error occurred while inviting the user", - "userInvited": "User invited", - "userInvitedDescription": "The user has been successfully invited.", - "userErrorCreate": "Failed to create user", - "userErrorCreateDescription": "An error occurred while creating the user", - "userCreated": "User created", - "userCreatedDescription": "The user has been successfully created.", - "userTypeInternal": "Internal User", - "userTypeInternalDescription": "Invite a user to join your organization directly.", - "userTypeExternal": "External User", - "userTypeExternalDescription": "Create a user with an external identity provider.", - "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", - "userSettings": "User Information", - "userSettingsDescription": "Enter the details for the new user", - "inviteEmailSent": "Send invite email to user", - "inviteValid": "Valid For", - "selectDuration": "Select duration", - "accessRoleSelect": "Select role", - "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", - "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", - "inviteExpiresIn": "The invite will expire in {days, plural, one {# day} other {# days}}.", - "idpTitle": "Identity Provider", - "idpSelect": "Select the identity provider for the external user", - "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", - "usernameUniq": "This must match the unique username that exists in the selected identity provider.", - "emailOptional": "Email (Optional)", - "nameOptional": "Name (Optional)", - "accessControls": "Access Controls", - "userDescription2": "Manage the settings on this user", - "accessRoleErrorAdd": "Failed to add user to role", - "accessRoleErrorAddDescription": "An error occurred while adding user to the role.", - "userSaved": "User saved", - "userSavedDescription": "The user has been updated.", - "autoProvisioned": "Auto Provisioned", - "autoProvisionedDescription": "Allow this user to be automatically managed by identity provider", - "accessControlsDescription": "Manage what this user can access and do in the organization", - "accessControlsSubmit": "Save Access Controls", - "roles": "Roles", - "accessUsersRoles": "Manage Users & Roles", - "accessUsersRolesDescription": "Invite users and add them to roles to manage access to your organization", - "key": "Key", - "createdAt": "Created At", - "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", - "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", - "proxyEnableSSL": "Enable SSL (https)", - "targetErrorFetch": "Failed to fetch targets", - "targetErrorFetchDescription": "An error occurred while fetching targets", - "siteErrorFetch": "Failed to fetch resource", - "siteErrorFetchDescription": "An error occurred while fetching resource", + "accessRoleErrorFetch": "Nepodařilo se načíst role", + "accessRoleErrorFetchDescription": "Při načítání rolí došlo k chybě", + "idpErrorFetch": "Nepodařilo se načíst poskytovatele identity", + "idpErrorFetchDescription": "Při načítání poskytovatelů identity došlo k chybě", + "userErrorExists": "Uživatel již existuje", + "userErrorExistsDescription": "Tento uživatel je již členem organizace.", + "inviteError": "Nepodařilo se pozvat uživatele", + "inviteErrorDescription": "Při pozvání uživatele došlo k chybě", + "userInvited": "Uživatel pozván", + "userInvitedDescription": "Uživatel byl úspěšně pozván.", + "userErrorCreate": "Nepodařilo se vytvořit uživatele", + "userErrorCreateDescription": "Došlo k chybě při vytváření uživatele", + "userCreated": "Uživatel byl vytvořen", + "userCreatedDescription": "Uživatel byl úspěšně vytvořen.", + "userTypeInternal": "Interní uživatel", + "userTypeInternalDescription": "Pozvěte uživatele do vaší organizace přímo.", + "userTypeExternal": "Externí uživatel", + "userTypeExternalDescription": "Vytvořte uživatele s externím poskytovatelem identity.", + "accessUserCreateDescription": "Postupujte podle níže uvedených kroků pro vytvoření nového uživatele", + "userSeeAll": "Zobrazit všechny uživatele", + "userTypeTitle": "Typ uživatele", + "userTypeDescription": "Určete, jak chcete vytvořit uživatele", + "userSettings": "Informace o uživateli", + "userSettingsDescription": "Zadejte podrobnosti pro nového uživatele", + "inviteEmailSent": "Poslat uživateli pozvánku", + "inviteValid": "Platné pro", + "selectDuration": "Vyberte dobu trvání", + "accessRoleSelect": "Vybrat roli", + "inviteEmailSentDescription": "Uživateli byl odeslán e-mail s odkazem pro přístup níže. Pro přijetí pozvánky musí mít přístup k odkazu.", + "inviteSentDescription": "Uživatel byl pozván. Pro přijetí pozvánky musí mít přístup na níže uvedený odkaz.", + "inviteExpiresIn": "Pozvánka vyprší za {days, plural, one {# den} other {# days}}.", + "idpTitle": "Poskytovatel identity", + "idpSelect": "Vyberte poskytovatele identity pro externího uživatele", + "idpNotConfigured": "Nejsou nakonfigurováni žádní poskytovatelé identity. Před vytvořením externích uživatelů prosím nakonfigurujte poskytovatele identity.", + "usernameUniq": "Toto musí odpovídat jedinečné uživatelské jméno, které existuje ve vybraném poskytovateli identity.", + "emailOptional": "E-mail (nepovinné)", + "nameOptional": "Jméno (nepovinné)", + "accessControls": "Kontrola přístupu", + "userDescription2": "Spravovat nastavení tohoto uživatele", + "accessRoleErrorAdd": "Přidání uživatele do role se nezdařilo", + "accessRoleErrorAddDescription": "Došlo k chybě při přidávání uživatele do role.", + "userSaved": "Uživatel uložen", + "userSavedDescription": "Uživatel byl aktualizován.", + "autoProvisioned": "Automaticky poskytnuto", + "autoProvisionedDescription": "Povolit tomuto uživateli automaticky spravovat poskytovatel identity", + "accessControlsDescription": "Spravovat co může tento uživatel přistupovat a dělat v organizaci", + "accessControlsSubmit": "Uložit kontroly přístupu", + "roles": "Role", + "accessUsersRoles": "Spravovat uživatele a role", + "accessUsersRolesDescription": "Pozvěte uživatele a přidejte je do rolí pro správu přístupu do vaší organizace", + "key": "Klíč", + "createdAt": "Vytvořeno v", + "proxyErrorInvalidHeader": "Neplatná hodnota hlavičky hostitele. Použijte formát názvu domény, nebo uložte prázdné pro zrušení vlastního hlavičky hostitele.", + "proxyErrorTls": "Neplatné jméno TLS serveru. Použijte formát doménového jména nebo uložte prázdné pro odstranění názvu TLS serveru.", + "proxyEnableSSL": "Povolit SSL (https)", + "targetErrorFetch": "Nepodařilo se načíst cíle", + "targetErrorFetchDescription": "Při načítání cílů došlo k chybě", + "siteErrorFetch": "Nepodařilo se načíst zdroj", + "siteErrorFetchDescription": "Při načítání zdroje došlo k chybě", "targetErrorDuplicate": "Duplicate target", - "targetErrorDuplicateDescription": "A target with these settings already exists", + "targetErrorDuplicateDescription": "Cíl s těmito nastaveními již existuje", "targetWireGuardErrorInvalidIp": "Invalid target IP", - "targetWireGuardErrorInvalidIpDescription": "Target IP must be within the site subnet", - "targetsUpdated": "Targets updated", - "targetsUpdatedDescription": "Targets and settings updated successfully", - "targetsErrorUpdate": "Failed to update targets", - "targetsErrorUpdateDescription": "An error occurred while updating targets", - "targetTlsUpdate": "TLS settings updated", - "targetTlsUpdateDescription": "Your TLS settings have been updated successfully", - "targetErrorTlsUpdate": "Failed to update TLS settings", - "targetErrorTlsUpdateDescription": "An error occurred while updating TLS settings", - "proxyUpdated": "Proxy settings updated", - "proxyUpdatedDescription": "Your proxy settings have been updated successfully", - "proxyErrorUpdate": "Failed to update proxy settings", - "proxyErrorUpdateDescription": "An error occurred while updating proxy settings", + "targetWireGuardErrorInvalidIpDescription": "Cílová IP adresa musí být v podsíti webu", + "targetsUpdated": "Cíle byly aktualizovány", + "targetsUpdatedDescription": "Cíle a nastavení byly úspěšně aktualizovány", + "targetsErrorUpdate": "Nepodařilo se aktualizovat cíle", + "targetsErrorUpdateDescription": "Došlo k chybě při aktualizaci cílů", + "targetTlsUpdate": "Nastavení TLS aktualizováno", + "targetTlsUpdateDescription": "Vaše nastavení TLS bylo úspěšně aktualizováno", + "targetErrorTlsUpdate": "Aktualizace nastavení TLS se nezdařila", + "targetErrorTlsUpdateDescription": "Došlo k chybě při aktualizaci nastavení TLS", + "proxyUpdated": "Nastavení proxy bylo aktualizováno", + "proxyUpdatedDescription": "Vaše nastavení proxy bylo úspěšně aktualizováno", + "proxyErrorUpdate": "Aktualizace nastavení proxy se nezdařila", + "proxyErrorUpdateDescription": "Došlo k chybě při aktualizaci nastavení proxy", "targetAddr": "IP / Hostname", - "targetPort": "Port", - "targetProtocol": "Protocol", - "targetTlsSettings": "Secure Connection Configuration", - "targetTlsSettingsDescription": "Configure SSL/TLS settings for your resource", - "targetTlsSettingsAdvanced": "Advanced TLS Settings", - "targetTlsSni": "TLS Server Name (SNI)", - "targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.", - "targetTlsSubmit": "Save Settings", - "targets": "Targets Configuration", - "targetsDescription": "Set up targets to route traffic to your backend services", - "targetStickySessions": "Enable Sticky Sessions", - "targetStickySessionsDescription": "Keep connections on the same backend target for their entire session.", - "methodSelect": "Select method", + "targetPort": "Přístav", + "targetProtocol": "Protokol", + "targetTlsSettings": "Nastavení bezpečného připojení", + "targetTlsSettingsDescription": "Konfigurace nastavení SSL/TLS pro váš dokument", + "targetTlsSettingsAdvanced": "Pokročilé nastavení TLS", + "targetTlsSni": "Název serveru TLS (SNI)", + "targetTlsSniDescription": "Název serveru TLS pro použití v SNI. Ponechte prázdné pro použití výchozího nastavení.", + "targetTlsSubmit": "Uložit nastavení", + "targets": "Konfigurace cílů", + "targetsDescription": "Nastavte cíle pro směrování provozu do záložních služeb", + "targetStickySessions": "Povolit Rychlé relace", + "targetStickySessionsDescription": "Zachovat spojení na stejném cíli pro celou relaci.", + "methodSelect": "Vyberte metodu", "targetSubmit": "Add Target", - "targetNoOne": "No targets. Add a target using the form.", - "targetNoOneDescription": "Adding more than one target above will enable load balancing.", - "targetsSubmit": "Save Targets", - "proxyAdditional": "Additional Proxy Settings", - "proxyAdditionalDescription": "Configure how your resource handles proxy settings", - "proxyCustomHeader": "Custom Host Header", - "proxyCustomHeaderDescription": "The host header to set when proxying requests. Leave empty to use the default.", - "proxyAdditionalSubmit": "Save Proxy Settings", - "subnetMaskErrorInvalid": "Invalid subnet mask. Must be between 0 and 32.", - "ipAddressErrorInvalidFormat": "Invalid IP address format", - "ipAddressErrorInvalidOctet": "Invalid IP address octet", - "path": "Path", - "matchPath": "Match Path", - "ipAddressRange": "IP Range", - "rulesErrorFetch": "Failed to fetch rules", - "rulesErrorFetchDescription": "An error occurred while fetching rules", - "rulesErrorDuplicate": "Duplicate rule", - "rulesErrorDuplicateDescription": "A rule with these settings already exists", - "rulesErrorInvalidIpAddressRange": "Invalid CIDR", - "rulesErrorInvalidIpAddressRangeDescription": "Please enter a valid CIDR value", - "rulesErrorInvalidUrl": "Invalid URL path", - "rulesErrorInvalidUrlDescription": "Please enter a valid URL path value", - "rulesErrorInvalidIpAddress": "Invalid IP", - "rulesErrorInvalidIpAddressDescription": "Please enter a valid IP address", - "rulesErrorUpdate": "Failed to update rules", - "rulesErrorUpdateDescription": "An error occurred while updating rules", - "rulesUpdated": "Enable Rules", - "rulesUpdatedDescription": "Rule evaluation has been updated", - "rulesMatchIpAddressRangeDescription": "Enter an address in CIDR format (e.g., 103.21.244.0/22)", - "rulesMatchIpAddress": "Enter an IP address (e.g., 103.21.244.12)", - "rulesMatchUrl": "Enter a URL path or pattern (e.g., /api/v1/todos or /api/v1/*)", - "rulesErrorInvalidPriority": "Invalid Priority", - "rulesErrorInvalidPriorityDescription": "Please enter a valid priority", - "rulesErrorDuplicatePriority": "Duplicate Priorities", - "rulesErrorDuplicatePriorityDescription": "Please enter unique priorities", - "ruleUpdated": "Rules updated", - "ruleUpdatedDescription": "Rules updated successfully", - "ruleErrorUpdate": "Operation failed", - "ruleErrorUpdateDescription": "An error occurred during the save operation", - "rulesPriority": "Priority", - "rulesAction": "Action", - "rulesMatchType": "Match Type", - "value": "Value", - "rulesAbout": "About Rules", - "rulesAboutDescription": "Rules allow you to control access to your resource based on a set of criteria. You can create rules to allow or deny access based on IP address or URL path.", - "rulesActions": "Actions", - "rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods", - "rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted", - "rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted", - "rulesMatchCriteria": "Matching Criteria", - "rulesMatchCriteriaIpAddress": "Match a specific IP address", - "rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation", - "rulesMatchCriteriaUrl": "Match a URL path or pattern", - "rulesEnable": "Enable Rules", - "rulesEnableDescription": "Enable or disable rule evaluation for this resource", - "rulesResource": "Resource Rules Configuration", - "rulesResourceDescription": "Configure rules to control access to your resource", - "ruleSubmit": "Add Rule", - "rulesNoOne": "No rules. Add a rule using the form.", - "rulesOrder": "Rules are evaluated by priority in ascending order.", - "rulesSubmit": "Save Rules", - "resourceErrorCreate": "Error creating resource", - "resourceErrorCreateDescription": "An error occurred when creating the resource", - "resourceErrorCreateMessage": "Error creating resource:", - "resourceErrorCreateMessageDescription": "An unexpected error occurred", - "sitesErrorFetch": "Error fetching sites", - "sitesErrorFetchDescription": "An error occurred when fetching the sites", - "domainsErrorFetch": "Error fetching domains", - "domainsErrorFetchDescription": "An error occurred when fetching the domains", - "none": "None", - "unknown": "Unknown", - "resources": "Resources", - "resourcesDescription": "Resources are proxies to applications running on your private network. Create a resource for any HTTP/HTTPS or raw TCP/UDP service on your private network. Each resource must be connected to a site to enable private, secure connectivity through an encrypted WireGuard tunnel.", - "resourcesWireGuardConnect": "Secure connectivity with WireGuard encryption", - "resourcesMultipleAuthenticationMethods": "Configure multiple authentication methods", - "resourcesUsersRolesAccess": "User and role-based access control", - "resourcesErrorUpdate": "Failed to toggle resource", - "resourcesErrorUpdateDescription": "An error occurred while updating the resource", - "access": "Access", - "shareLink": "{resource} Share Link", - "resourceSelect": "Select resource", - "shareLinks": "Share Links", - "share": "Shareable Links", - "shareDescription2": "Create shareable links to your resources. Links provide temporary or unlimited access to your resource. You can configure the expiration duration of the link when you create one.", - "shareEasyCreate": "Easy to create and share", - "shareConfigurableExpirationDuration": "Configurable expiration duration", - "shareSecureAndRevocable": "Secure and revocable", - "nameMin": "Name must be at least {len} characters.", - "nameMax": "Name must not be longer than {len} characters.", - "sitesConfirmCopy": "Please confirm that you have copied the config.", - "unknownCommand": "Unknown command", - "newtErrorFetchReleases": "Failed to fetch release info: {err}", - "newtErrorFetchLatest": "Error fetching latest release: {err}", + "targetNoOne": "Žádné cíle. Přidejte cíl pomocí formuláře.", + "targetNoOneDescription": "Přidáním více než jednoho cíle se umožní vyvážení zatížení.", + "targetsSubmit": "Uložit cíle", + "proxyAdditional": "Další nastavení proxy", + "proxyAdditionalDescription": "Konfigurovat nastavení proxy zpracování vašeho zdroje", + "proxyCustomHeader": "Vlastní hlavička hostitele", + "proxyCustomHeaderDescription": "Hlavička hostitele bude nastavena při proxování požadavků. Nechte prázdné pro použití výchozího nastavení.", + "proxyAdditionalSubmit": "Uložit nastavení proxy", + "subnetMaskErrorInvalid": "Neplatná maska subsítě. Musí být mezi 0 a 32.", + "ipAddressErrorInvalidFormat": "Neplatný formát IP adresy", + "ipAddressErrorInvalidOctet": "Neplatná IP adresa octet", + "path": "Cesta", + "matchPath": "Cesta k zápasu", + "ipAddressRange": "Rozsah IP", + "rulesErrorFetch": "Nepodařilo se načíst pravidla", + "rulesErrorFetchDescription": "Při načítání pravidel došlo k chybě", + "rulesErrorDuplicate": "Duplikovat pravidlo", + "rulesErrorDuplicateDescription": "Pravidlo s těmito nastaveními již existuje", + "rulesErrorInvalidIpAddressRange": "Neplatný CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "Zadejte prosím platnou hodnotu CIDR", + "rulesErrorInvalidUrl": "Neplatná URL cesta", + "rulesErrorInvalidUrlDescription": "Zadejte platnou hodnotu URL cesty", + "rulesErrorInvalidIpAddress": "Neplatná IP adresa", + "rulesErrorInvalidIpAddressDescription": "Zadejte prosím platnou IP adresu", + "rulesErrorUpdate": "Aktualizace pravidel se nezdařila", + "rulesErrorUpdateDescription": "Při aktualizaci pravidel došlo k chybě", + "rulesUpdated": "Povolit pravidla", + "rulesUpdatedDescription": "Hodnocení pravidel bylo aktualizováno", + "rulesMatchIpAddressRangeDescription": "Zadejte adresu ve formátu CIDR (např. 103.21.244.0/22)", + "rulesMatchIpAddress": "Zadejte IP adresu (např. 103.21.244.12)", + "rulesMatchUrl": "Zadejte URL cestu nebo vzor (např. /api/v1/todos nebo /api/v1/*)", + "rulesErrorInvalidPriority": "Neplatná Priorita", + "rulesErrorInvalidPriorityDescription": "Zadejte prosím platnou prioritu", + "rulesErrorDuplicatePriority": "Duplikovat priority", + "rulesErrorDuplicatePriorityDescription": "Zadejte prosím unikátní priority", + "ruleUpdated": "Pravidla byla aktualizována", + "ruleUpdatedDescription": "Pravidla byla úspěšně aktualizována", + "ruleErrorUpdate": "Operace selhala", + "ruleErrorUpdateDescription": "Při ukládání došlo k chybě", + "rulesPriority": "Priorita", + "rulesAction": "Akce", + "rulesMatchType": "Typ shody", + "value": "Hodnota", + "rulesAbout": "O pravidlech", + "rulesAboutDescription": "Pravidla vám umožňují kontrolovat přístup k vašemu zdroji na základě sady kritérií. Můžete vytvořit pravidla pro povolení nebo zamítnutí přístupu na základě IP adresy nebo cesty URL.", + "rulesActions": "Akce", + "rulesActionAlwaysAllow": "Vždy Povolit: Obejít všechny metody ověřování", + "rulesActionAlwaysDeny": "Vždy odepří: Zablokovat všechny požadavky; nelze se pokusit o ověření", + "rulesActionPassToAuth": "Pass to Auth: Povolit autentizační metody", + "rulesMatchCriteria": "Odpovídající kritéria", + "rulesMatchCriteriaIpAddress": "Porovnat konkrétní IP adresu", + "rulesMatchCriteriaIpAddressRange": "Odpovídá rozsahu IP adres v CIDR značení", + "rulesMatchCriteriaUrl": "Porovnejte URL cestu nebo gesto", + "rulesEnable": "Povolit pravidla", + "rulesEnableDescription": "Povolit nebo zakázat hodnocení pravidel pro tento zdroj", + "rulesResource": "Konfigurace pravidel zdroje", + "rulesResourceDescription": "Konfigurace pravidel pro kontrolu přístupu k vašemu zdroji", + "ruleSubmit": "Přidat pravidlo", + "rulesNoOne": "Žádná pravidla. Přidejte pravidlo pomocí formuláře.", + "rulesOrder": "Pravidla jsou hodnocena podle priority vzestupně.", + "rulesSubmit": "Uložit pravidla", + "resourceErrorCreate": "Chyba při vytváření zdroje", + "resourceErrorCreateDescription": "Při vytváření zdroje došlo k chybě", + "resourceErrorCreateMessage": "Chyba při vytváření zdroje:", + "resourceErrorCreateMessageDescription": "Došlo k neočekávané chybě", + "sitesErrorFetch": "Chyba při načítání stránek", + "sitesErrorFetchDescription": "Při načítání stránek došlo k chybě", + "domainsErrorFetch": "Chyba při načítání domén", + "domainsErrorFetchDescription": "Při načítání domén došlo k chybě", + "none": "Nic", + "unknown": "Neznámý", + "resources": "Zdroje", + "resourcesDescription": "Zdroje jsou proxy aplikací běžících na vaší soukromé síti. Vytvořte zdroj pro jakoukoli HTTP/HTTPS nebo nakreslete TCP/UDP službu na vaší soukromé síti. Každý zdroj musí být připojen k webu pro povolení soukromého, zabezpečeného připojení pomocí šifrovaného tunelu WireGuard.", + "resourcesWireGuardConnect": "Bezpečné připojení s šifrováním WireGuard", + "resourcesMultipleAuthenticationMethods": "Konfigurace vícenásobných metod ověřování", + "resourcesUsersRolesAccess": "Kontrola přístupu na základě uživatelů a rolí", + "resourcesErrorUpdate": "Nepodařilo se přepnout zdroj", + "resourcesErrorUpdateDescription": "Došlo k chybě při aktualizaci zdroje", + "access": "Přístup", + "shareLink": "{resource} Sdílet odkaz", + "resourceSelect": "Vyberte zdroj", + "shareLinks": "Sdílet odkazy", + "share": "Sdílené odkazy", + "shareDescription2": "Vytvořte sdílitelné odkazy na vaše zdroje. Odkazy poskytují dočasný nebo neomezený přístup k vašemu zdroji. Můžete nakonfigurovat dobu vypršení platnosti odkazu při jeho vytvoření.", + "shareEasyCreate": "Snadné vytváření a sdílení", + "shareConfigurableExpirationDuration": "Konfigurovatelná doba vypršení platnosti", + "shareSecureAndRevocable": "Bezpečné a odvolatelné", + "nameMin": "Jméno musí být alespoň {len} znaků.", + "nameMax": "Název nesmí být delší než {len} znaků.", + "sitesConfirmCopy": "Potvrďte, že jste zkopírovali konfiguraci.", + "unknownCommand": "Neznámý příkaz", + "newtErrorFetchReleases": "Nepodařilo se načíst informace o vydání: {err}", + "newtErrorFetchLatest": "Chyba při načítání nejnovější verze: {err}", "newtEndpoint": "Newt Endpoint", "newtId": "Newt ID", - "newtSecretKey": "Newt Secret Key", - "architecture": "Architecture", - "sites": "Sites", - "siteWgAnyClients": "Use any WireGuard client to connect. You will have to address your internal resources using the peer IP.", - "siteWgCompatibleAllClients": "Compatible with all WireGuard clients", - "siteWgManualConfigurationRequired": "Manual configuration required", - "userErrorNotAdminOrOwner": "User is not an admin or owner", - "pangolinSettings": "Settings - Pangolin", - "accessRoleYour": "Your role:", - "accessRoleSelect2": "Select a role", - "accessUserSelect": "Select a user", - "otpEmailEnter": "Enter an email", - "otpEmailEnterDescription": "Press enter to add an email after typing it in the input field.", - "otpEmailErrorInvalid": "Invalid email address. Wildcard (*) must be the entire local part.", - "otpEmailSmtpRequired": "SMTP Required", - "otpEmailSmtpRequiredDescription": "SMTP must be enabled on the server to use one-time password authentication.", - "otpEmailTitle": "One-time Passwords", - "otpEmailTitleDescription": "Require email-based authentication for resource access", - "otpEmailWhitelist": "Email Whitelist", - "otpEmailWhitelistList": "Whitelisted Emails", - "otpEmailWhitelistListDescription": "Only users with these email addresses will be able to access this resource. They will be prompted to enter a one-time password sent to their email. Wildcards (*@example.com) can be used to allow any email address from a domain.", - "otpEmailWhitelistSave": "Save Whitelist", - "passwordAdd": "Add Password", - "passwordRemove": "Remove Password", - "pincodeAdd": "Add PIN Code", - "pincodeRemove": "Remove PIN Code", - "resourceAuthMethods": "Authentication Methods", - "resourceAuthMethodsDescriptions": "Allow access to the resource via additional auth methods", - "resourceAuthSettingsSave": "Saved successfully", - "resourceAuthSettingsSaveDescription": "Authentication settings have been saved", - "resourceErrorAuthFetch": "Failed to fetch data", - "resourceErrorAuthFetchDescription": "An error occurred while fetching the data", - "resourceErrorPasswordRemove": "Error removing resource password", - "resourceErrorPasswordRemoveDescription": "An error occurred while removing the resource password", - "resourceErrorPasswordSetup": "Error setting resource password", - "resourceErrorPasswordSetupDescription": "An error occurred while setting the resource password", - "resourceErrorPincodeRemove": "Error removing resource pincode", - "resourceErrorPincodeRemoveDescription": "An error occurred while removing the resource pincode", - "resourceErrorPincodeSetup": "Error setting resource PIN code", - "resourceErrorPincodeSetupDescription": "An error occurred while setting the resource PIN code", - "resourceErrorUsersRolesSave": "Failed to set roles", - "resourceErrorUsersRolesSaveDescription": "An error occurred while setting the roles", - "resourceErrorWhitelistSave": "Failed to save whitelist", - "resourceErrorWhitelistSaveDescription": "An error occurred while saving the whitelist", - "resourcePasswordSubmit": "Enable Password Protection", - "resourcePasswordProtection": "Password Protection {status}", - "resourcePasswordRemove": "Resource password removed", - "resourcePasswordRemoveDescription": "The resource password has been removed successfully", - "resourcePasswordSetup": "Resource password set", - "resourcePasswordSetupDescription": "The resource password has been set successfully", - "resourcePasswordSetupTitle": "Set Password", - "resourcePasswordSetupTitleDescription": "Set a password to protect this resource", - "resourcePincode": "PIN Code", - "resourcePincodeSubmit": "Enable PIN Code Protection", - "resourcePincodeProtection": "PIN Code Protection {status}", - "resourcePincodeRemove": "Resource pincode removed", - "resourcePincodeRemoveDescription": "The resource password has been removed successfully", - "resourcePincodeSetup": "Resource PIN code set", - "resourcePincodeSetupDescription": "The resource pincode has been set successfully", - "resourcePincodeSetupTitle": "Set Pincode", - "resourcePincodeSetupTitleDescription": "Set a pincode to protect this resource", - "resourceRoleDescription": "Admins can always access this resource.", - "resourceUsersRoles": "Users & Roles", - "resourceUsersRolesDescription": "Configure which users and roles can visit this resource", - "resourceUsersRolesSubmit": "Save Users & Roles", - "resourceWhitelistSave": "Saved successfully", - "resourceWhitelistSaveDescription": "Whitelist settings have been saved", - "ssoUse": "Use Platform SSO", - "ssoUseDescription": "Existing users will only have to log in once for all resources that have this enabled.", - "proxyErrorInvalidPort": "Invalid port number", - "subdomainErrorInvalid": "Invalid subdomain", - "domainErrorFetch": "Error fetching domains", - "domainErrorFetchDescription": "An error occurred when fetching the domains", - "resourceErrorUpdate": "Failed to update resource", - "resourceErrorUpdateDescription": "An error occurred while updating the resource", - "resourceUpdated": "Resource updated", - "resourceUpdatedDescription": "The resource has been updated successfully", - "resourceErrorTransfer": "Failed to transfer resource", - "resourceErrorTransferDescription": "An error occurred while transferring the resource", - "resourceTransferred": "Resource transferred", - "resourceTransferredDescription": "The resource has been transferred successfully", - "resourceErrorToggle": "Failed to toggle resource", - "resourceErrorToggleDescription": "An error occurred while updating the resource", - "resourceVisibilityTitle": "Visibility", - "resourceVisibilityTitleDescription": "Completely enable or disable resource visibility", - "resourceGeneral": "General Settings", - "resourceGeneralDescription": "Configure the general settings for this resource", - "resourceEnable": "Enable Resource", - "resourceTransfer": "Transfer Resource", - "resourceTransferDescription": "Transfer this resource to a different site", - "resourceTransferSubmit": "Transfer Resource", - "siteDestination": "Destination Site", - "searchSites": "Search sites", - "accessRoleCreate": "Create Role", - "accessRoleCreateDescription": "Create a new role to group users and manage their permissions.", - "accessRoleCreateSubmit": "Create Role", - "accessRoleCreated": "Role created", - "accessRoleCreatedDescription": "The role has been successfully created.", - "accessRoleErrorCreate": "Failed to create role", - "accessRoleErrorCreateDescription": "An error occurred while creating the role.", - "accessRoleErrorNewRequired": "New role is required", - "accessRoleErrorRemove": "Failed to remove role", - "accessRoleErrorRemoveDescription": "An error occurred while removing the role.", - "accessRoleName": "Role Name", - "accessRoleQuestionRemove": "You're about to delete the {name} role. You cannot undo this action.", - "accessRoleRemove": "Remove Role", - "accessRoleRemoveDescription": "Remove a role from the organization", - "accessRoleRemoveSubmit": "Remove Role", - "accessRoleRemoved": "Role removed", - "accessRoleRemovedDescription": "The role has been successfully removed.", - "accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.", - "manage": "Manage", - "sitesNotFound": "No sites found.", - "pangolinServerAdmin": "Server Admin - Pangolin", - "licenseTierProfessional": "Professional License", - "licenseTierEnterprise": "Enterprise License", - "licenseTierCommercial": "Commercial License", - "licensed": "Licensed", - "yes": "Yes", - "no": "No", - "sitesAdditional": "Additional Sites", - "licenseKeys": "License Keys", - "sitestCountDecrease": "Decrease site count", - "sitestCountIncrease": "Increase site count", - "idpManage": "Manage Identity Providers", - "idpManageDescription": "View and manage identity providers in the system", - "idpDeletedDescription": "Identity provider deleted successfully", + "newtSecretKey": "Tajný klíč novinky", + "architecture": "Architektura", + "sites": "Stránky", + "siteWgAnyClients": "K připojení použijte jakéhokoli klienta WireGuard. Budete muset řešit své interní zdroje pomocí klientské IP adresy.", + "siteWgCompatibleAllClients": "Kompatibilní se všemi klienty aplikace WireGuard", + "siteWgManualConfigurationRequired": "Je vyžadována ruční konfigurace", + "userErrorNotAdminOrOwner": "Uživatel není administrátor nebo vlastník", + "pangolinSettings": "Nastavení - Pangolin", + "accessRoleYour": "Vaše role:", + "accessRoleSelect2": "Vyberte roli", + "accessUserSelect": "Vyberte uživatele", + "otpEmailEnter": "Zadejte e-mail", + "otpEmailEnterDescription": "Stisknutím klávesy Enter přidáte e-mail po zadání do vstupního pole.", + "otpEmailErrorInvalid": "Neplatná e-mailová adresa. Wildcard (*) musí být celá místní část.", + "otpEmailSmtpRequired": "Vyžadováno SMTP", + "otpEmailSmtpRequiredDescription": "Pro použití jednorázového ověření heslem musí být na serveru povolen SMTP.", + "otpEmailTitle": "Jednorázová hesla", + "otpEmailTitleDescription": "Vyžadovat ověření pomocí e-mailu pro přístup ke zdrojům", + "otpEmailWhitelist": "Whitelist e-mailu", + "otpEmailWhitelistList": "Povolené e-maily", + "otpEmailWhitelistListDescription": "K tomuto zdroji budou mít přístup pouze uživatelé s těmito e-mailovými adresami. Budou vyzváni k zadání jednorázového hesla, které bude odesláno na svůj e-mail. Wildcards (*@example.com) lze použít pro povolení jakékoli e-mailové adresy z domény.", + "otpEmailWhitelistSave": "Uložit seznam povolených", + "passwordAdd": "Přidat heslo", + "passwordRemove": "Odstranit heslo", + "pincodeAdd": "Přidat PIN kód", + "pincodeRemove": "Odstranit PIN kód", + "resourceAuthMethods": "Metody ověřování", + "resourceAuthMethodsDescriptions": "Povolit přístup ke zdroji pomocí dodatečných metod autorizace", + "resourceAuthSettingsSave": "Úspěšně uloženo", + "resourceAuthSettingsSaveDescription": "Nastavení ověřování bylo uloženo", + "resourceErrorAuthFetch": "Nepodařilo se načíst data", + "resourceErrorAuthFetchDescription": "Při načítání dat došlo k chybě", + "resourceErrorPasswordRemove": "Chyba při odstraňování hesla zdroje", + "resourceErrorPasswordRemoveDescription": "Došlo k chybě při odstraňování hesla", + "resourceErrorPasswordSetup": "Chyba při nastavování hesla", + "resourceErrorPasswordSetupDescription": "Při nastavování hesla došlo k chybě", + "resourceErrorPincodeRemove": "Chyba při odstraňování zdrojového kódu", + "resourceErrorPincodeRemoveDescription": "Došlo k chybě při odstraňování zdroje pincode", + "resourceErrorPincodeSetup": "Chyba při nastavení zdrojového PIN kódu", + "resourceErrorPincodeSetupDescription": "Došlo k chybě při nastavování zdrojového PIN kódu", + "resourceErrorUsersRolesSave": "Nepodařilo se nastavit role", + "resourceErrorUsersRolesSaveDescription": "Při nastavování rolí došlo k chybě", + "resourceErrorWhitelistSave": "Nepodařilo se uložit seznam povolených", + "resourceErrorWhitelistSaveDescription": "Došlo k chybě při ukládání seznamu povolených", + "resourcePasswordSubmit": "Povolit ochranu heslem", + "resourcePasswordProtection": "Ochrana hesla {status}", + "resourcePasswordRemove": "Heslo zdroje odstraněno", + "resourcePasswordRemoveDescription": "Heslo zdroje bylo úspěšně odstraněno", + "resourcePasswordSetup": "Heslo zdroje nastaveno", + "resourcePasswordSetupDescription": "Heslo zdroje bylo úspěšně nastaveno", + "resourcePasswordSetupTitle": "Nastavit heslo", + "resourcePasswordSetupTitleDescription": "Nastavte heslo pro ochranu tohoto zdroje", + "resourcePincode": "PIN kód", + "resourcePincodeSubmit": "Povolit ochranu PIN kódu", + "resourcePincodeProtection": "Ochrana kódu PIN {status}", + "resourcePincodeRemove": "Zdroj byl odstraněn", + "resourcePincodeRemoveDescription": "Heslo zdroje bylo úspěšně odstraněno", + "resourcePincodeSetup": "PIN kód zdroje nastaven", + "resourcePincodeSetupDescription": "Zdroj byl úspěšně nastaven", + "resourcePincodeSetupTitle": "Nastavit anonymní kód", + "resourcePincodeSetupTitleDescription": "Nastavit pincode pro ochranu tohoto zdroje", + "resourceRoleDescription": "Administrátoři mají vždy přístup k tomuto zdroji.", + "resourceUsersRoles": "Uživatelé a role", + "resourceUsersRolesDescription": "Nastavení, kteří uživatelé a role mohou navštívit tento zdroj", + "resourceUsersRolesSubmit": "Uložit uživatele a role", + "resourceWhitelistSave": "Úspěšně uloženo", + "resourceWhitelistSaveDescription": "Nastavení seznamu povolených položek bylo uloženo", + "ssoUse": "Použít platformu SSO", + "ssoUseDescription": "Existující uživatelé se budou muset přihlásit pouze jednou pro všechny zdroje, které jsou povoleny.", + "proxyErrorInvalidPort": "Neplatné číslo portu", + "subdomainErrorInvalid": "Neplatná subdoména", + "domainErrorFetch": "Chyba při načítání domén", + "domainErrorFetchDescription": "Při načítání domén došlo k chybě", + "resourceErrorUpdate": "Aktualizace zdroje se nezdařila", + "resourceErrorUpdateDescription": "Došlo k chybě při aktualizaci zdroje", + "resourceUpdated": "Zdroj byl aktualizován", + "resourceUpdatedDescription": "Zdroj byl úspěšně aktualizován", + "resourceErrorTransfer": "Nepodařilo se přenést dokument", + "resourceErrorTransferDescription": "Došlo k chybě při přenosu zdroje", + "resourceTransferred": "Přenesené zdroje", + "resourceTransferredDescription": "Zdroj byl úspěšně přenesen", + "resourceErrorToggle": "Nepodařilo se přepnout zdroj", + "resourceErrorToggleDescription": "Došlo k chybě při aktualizaci zdroje", + "resourceVisibilityTitle": "Viditelnost", + "resourceVisibilityTitleDescription": "Zcela povolit nebo zakázat viditelnost zdrojů", + "resourceGeneral": "Obecná nastavení", + "resourceGeneralDescription": "Konfigurace obecných nastavení tohoto zdroje", + "resourceEnable": "Povolit dokument", + "resourceTransfer": "Přenos zdroje", + "resourceTransferDescription": "Přenést tento zdroj na jiný web", + "resourceTransferSubmit": "Přenos zdroje", + "siteDestination": "Cílová stránka", + "searchSites": "Hledat lokality", + "accessRoleCreate": "Vytvořit roli", + "accessRoleCreateDescription": "Vytvořte novou roli pro seskupení uživatelů a spravujte jejich oprávnění.", + "accessRoleCreateSubmit": "Vytvořit roli", + "accessRoleCreated": "Role vytvořena", + "accessRoleCreatedDescription": "Role byla úspěšně vytvořena.", + "accessRoleErrorCreate": "Nepodařilo se vytvořit roli", + "accessRoleErrorCreateDescription": "Došlo k chybě při vytváření role.", + "accessRoleErrorNewRequired": "Je vyžadována nová role", + "accessRoleErrorRemove": "Nepodařilo se odstranit roli", + "accessRoleErrorRemoveDescription": "Došlo k chybě při odstraňování role.", + "accessRoleName": "Název role", + "accessRoleQuestionRemove": "Chystáte se odstranit {name} roli. Tuto akci nelze vrátit zpět.", + "accessRoleRemove": "Odstranit roli", + "accessRoleRemoveDescription": "Odebrat roli z organizace", + "accessRoleRemoveSubmit": "Odstranit roli", + "accessRoleRemoved": "Role odstraněna", + "accessRoleRemovedDescription": "Role byla úspěšně odstraněna.", + "accessRoleRequiredRemove": "Před odstraněním této role vyberte novou roli, do které chcete převést existující členy.", + "manage": "Spravovat", + "sitesNotFound": "Nebyly nalezeny žádné stránky.", + "pangolinServerAdmin": "Správce serveru - Pangolin", + "licenseTierProfessional": "Profesionální licence", + "licenseTierEnterprise": "Podniková licence", + "licenseTierCommercial": "Obchodní licence", + "licensed": "Licencováno", + "yes": "Ano", + "no": "Ne", + "sitesAdditional": "Další weby", + "licenseKeys": "Licenční klíče", + "sitestCountDecrease": "Snížit počet stránek", + "sitestCountIncrease": "Zvýšit počet stránek", + "idpManage": "Spravovat poskytovatele identity", + "idpManageDescription": "Zobrazit a spravovat poskytovatele identity v systému", + "idpDeletedDescription": "Poskytovatel identity byl úspěšně odstraněn", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?", - "idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.", - "idpMessageConfirm": "To confirm, please type the name of the identity provider below.", - "idpConfirmDelete": "Confirm Delete Identity Provider", - "idpDelete": "Delete Identity Provider", - "idp": "Identity Providers", - "idpSearch": "Search identity providers...", - "idpAdd": "Add Identity Provider", - "idpClientIdRequired": "Client ID is required.", - "idpClientSecretRequired": "Client Secret is required.", - "idpErrorAuthUrlInvalid": "Auth URL must be a valid URL.", - "idpErrorTokenUrlInvalid": "Token URL must be a valid URL.", - "idpPathRequired": "Identifier Path is required.", - "idpScopeRequired": "Scopes are required.", - "idpOidcDescription": "Configure an OpenID Connect identity provider", - "idpCreatedDescription": "Identity provider created successfully", - "idpCreate": "Create Identity Provider", - "idpCreateDescription": "Configure a new identity provider for user authentication", - "idpSeeAll": "See All Identity Providers", - "idpSettingsDescription": "Configure the basic information for your identity provider", - "idpDisplayName": "A display name for this identity provider", - "idpAutoProvisionUsers": "Auto Provision Users", - "idpAutoProvisionUsersDescription": "When enabled, users will be automatically created in the system upon first login with the ability to map users to roles and organizations.", - "licenseBadge": "Professional", - "idpType": "Provider Type", - "idpTypeDescription": "Select the type of identity provider you want to configure", - "idpOidcConfigure": "OAuth2/OIDC Configuration", - "idpOidcConfigureDescription": "Configure the OAuth2/OIDC provider endpoints and credentials", - "idpClientId": "Client ID", - "idpClientIdDescription": "The OAuth2 client ID from your identity provider", - "idpClientSecret": "Client Secret", - "idpClientSecretDescription": "The OAuth2 client secret from your identity provider", - "idpAuthUrl": "Authorization URL", - "idpAuthUrlDescription": "The OAuth2 authorization endpoint URL", - "idpTokenUrl": "Token URL", - "idpTokenUrlDescription": "The OAuth2 token endpoint URL", - "idpOidcConfigureAlert": "Important Information", - "idpOidcConfigureAlertDescription": "After creating the identity provider, you will need to configure the callback URL in your identity provider's settings. The callback URL will be provided after successful creation.", - "idpToken": "Token Configuration", - "idpTokenDescription": "Configure how to extract user information from the ID token", - "idpJmespathAbout": "About JMESPath", - "idpJmespathAboutDescription": "The paths below use JMESPath syntax to extract values from the ID token.", - "idpJmespathAboutDescriptionLink": "Learn more about JMESPath", - "idpJmespathLabel": "Identifier Path", - "idpJmespathLabelDescription": "The path to the user identifier in the ID token", - "idpJmespathEmailPathOptional": "Email Path (Optional)", - "idpJmespathEmailPathOptionalDescription": "The path to the user's email in the ID token", - "idpJmespathNamePathOptional": "Name Path (Optional)", - "idpJmespathNamePathOptionalDescription": "The path to the user's name in the ID token", - "idpOidcConfigureScopes": "Scopes", - "idpOidcConfigureScopesDescription": "Space-separated list of OAuth2 scopes to request", - "idpSubmit": "Create Identity Provider", - "orgPolicies": "Organization Policies", - "idpSettings": "{idpName} Settings", - "idpCreateSettingsDescription": "Configure the settings for your identity provider", - "roleMapping": "Role Mapping", - "orgMapping": "Organization Mapping", - "orgPoliciesSearch": "Search organization policies...", - "orgPoliciesAdd": "Add Organization Policy", - "orgRequired": "Organization is required", - "error": "Error", - "success": "Success", - "orgPolicyAddedDescription": "Policy added successfully", - "orgPolicyUpdatedDescription": "Policy updated successfully", - "orgPolicyDeletedDescription": "Policy deleted successfully", - "defaultMappingsUpdatedDescription": "Default mappings updated successfully", - "orgPoliciesAbout": "About Organization Policies", - "orgPoliciesAboutDescription": "Organization policies are used to control access to organizations based on the user's ID token. You can specify JMESPath expressions to extract role and organization information from the ID token.", - "orgPoliciesAboutDescriptionLink": "See documentation, for more information.", - "defaultMappingsOptional": "Default Mappings (Optional)", - "defaultMappingsOptionalDescription": "The default mappings are used when when there is not an organization policy defined for an organization. You can specify the default role and organization mappings to fall back to here.", - "defaultMappingsRole": "Default Role Mapping", - "defaultMappingsRoleDescription": "The result of this expression must return the role name as defined in the organization as a string.", - "defaultMappingsOrg": "Default Organization Mapping", - "defaultMappingsOrgDescription": "This expression must return the org ID or true for the user to be allowed to access the organization.", - "defaultMappingsSubmit": "Save Default Mappings", - "orgPoliciesEdit": "Edit Organization Policy", - "org": "Organization", - "orgSelect": "Select organization", - "orgSearch": "Search org", - "orgNotFound": "No org found.", - "roleMappingPathOptional": "Role Mapping Path (Optional)", - "orgMappingPathOptional": "Organization Mapping Path (Optional)", - "orgPolicyUpdate": "Update Policy", - "orgPolicyAdd": "Add Policy", - "orgPolicyConfig": "Configure access for an organization", - "idpUpdatedDescription": "Identity provider updated successfully", - "redirectUrl": "Redirect URL", - "redirectUrlAbout": "About Redirect URL", - "redirectUrlAboutDescription": "This is the URL to which users will be redirected after authentication. You need to configure this URL in your identity provider settings.", + "idpQuestionRemove": "Jste si jisti, že chcete trvale odstranit poskytovatele identity {name}?", + "idpMessageRemove": "Tímto odstraníte poskytovatele identity a všechny přidružené konfigurace. Uživatelé, kteří se autentizují prostřednictvím tohoto poskytovatele, se již nebudou moci přihlásit.", + "idpMessageConfirm": "Pro potvrzení zadejte níže uvedené jméno poskytovatele identity.", + "idpConfirmDelete": "Potvrdit odstranění poskytovatele identity", + "idpDelete": "Odstranit poskytovatele identity", + "idp": "Poskytovatelé identity", + "idpSearch": "Hledat poskytovatele identity...", + "idpAdd": "Přidat poskytovatele identity", + "idpClientIdRequired": "Je vyžadováno ID klienta.", + "idpClientSecretRequired": "Tajný klíč klienta je povinný.", + "idpErrorAuthUrlInvalid": "Ověřovací URL musí být platná adresa URL.", + "idpErrorTokenUrlInvalid": "URL tokenu musí být platná adresa URL.", + "idpPathRequired": "Je vyžadována cesta identifikátora.", + "idpScopeRequired": "Rozsahy jsou povinné.", + "idpOidcDescription": "Konfigurace OpenID Connect identitu poskytovatele", + "idpCreatedDescription": "Poskytovatel identity byl úspěšně vytvořen", + "idpCreate": "Vytvořit poskytovatele identity", + "idpCreateDescription": "Konfigurace nového poskytovatele identity pro ověření uživatele", + "idpSeeAll": "Zobrazit všechny poskytovatele identity", + "idpSettingsDescription": "Konfigurace základních informací pro svého poskytovatele identity", + "idpDisplayName": "Zobrazované jméno tohoto poskytovatele identity", + "idpAutoProvisionUsers": "Automatická úprava uživatelů", + "idpAutoProvisionUsersDescription": "Pokud je povoleno, uživatelé budou automaticky vytvářeni v systému při prvním přihlášení, s možností namapovat uživatele na role a organizace.", + "licenseBadge": "Profesionální", + "idpType": "Typ poskytovatele", + "idpTypeDescription": "Vyberte typ poskytovatele identity, který chcete nakonfigurovat", + "idpOidcConfigure": "Nastavení OAuth2/OIDC", + "idpOidcConfigureDescription": "Konfigurace koncových bodů a pověření poskytovatele OAuth2/OIDC", + "idpClientId": "ID klienta", + "idpClientIdDescription": "Klientské ID OAuth2 od poskytovatele identity", + "idpClientSecret": "Tajný klíč klienta", + "idpClientSecretDescription": "OAuth2 klíč klienta od poskytovatele identity", + "idpAuthUrl": "Autorizační URL", + "idpAuthUrlDescription": "Koncový koncový bod ověření OAuth2", + "idpTokenUrl": "URL tokenu", + "idpTokenUrlDescription": "URL koncového bodu OAuth2", + "idpOidcConfigureAlert": "Důležité informace", + "idpOidcConfigureAlertDescription": "Po vytvoření poskytovatele identity budete muset nakonfigurovat URL zpětného volání v nastavení poskytovatele identity. Adresa URL zpětného volání bude poskytnuta po úspěšném vytvoření.", + "idpToken": "Nastavení tokenu", + "idpTokenDescription": "Konfigurovat jak extrahovat informace o uživateli z ID tokenu", + "idpJmespathAbout": "O JMESPath", + "idpJmespathAboutDescription": "Cesty níže používají JMESPath syntax pro extrakci hodnot z ID tokenu.", + "idpJmespathAboutDescriptionLink": "Další informace o cestě JMESPath", + "idpJmespathLabel": "Cesta identifikátoru", + "idpJmespathLabelDescription": "Cesta k identifikátoru uživatele v tokenu ID", + "idpJmespathEmailPathOptional": "Cesta e-mailu (volitelné)", + "idpJmespathEmailPathOptionalDescription": "Cesta k e-mailu uživatele v ID tokenu", + "idpJmespathNamePathOptional": "Cesta k názvu (volitelné)", + "idpJmespathNamePathOptionalDescription": "Cesta ke jménu uživatele v identifikačním tokenu", + "idpOidcConfigureScopes": "Rozsah", + "idpOidcConfigureScopesDescription": "Seznam OAuth2 rozsahů oddělených mezerou", + "idpSubmit": "Vytvořit poskytovatele identity", + "orgPolicies": "Zásady organizace", + "idpSettings": "Nastavení {idpName}", + "idpCreateSettingsDescription": "Konfigurace nastavení pro poskytovatele identity", + "roleMapping": "Mapování rolí", + "orgMapping": "Mapování organizace", + "orgPoliciesSearch": "Hledat v zásadách organizace...", + "orgPoliciesAdd": "Přidat zásady organizace", + "orgRequired": "Organizace je povinná", + "error": "Chyba", + "success": "Úspěšně", + "orgPolicyAddedDescription": "Politika byla úspěšně přidána", + "orgPolicyUpdatedDescription": "Zásady byly úspěšně aktualizovány", + "orgPolicyDeletedDescription": "Zásady byly úspěšně odstraněny", + "defaultMappingsUpdatedDescription": "Výchozí mapování bylo úspěšně aktualizováno", + "orgPoliciesAbout": "O zásadách organizace", + "orgPoliciesAboutDescription": "Zásady organizace se používají pro kontrolu přístupu do organizací na základě identifikačního tokenu. Můžete zadat JMESPath výrazy pro extrahování rolí a informací o organizaci z ID tokenu.", + "orgPoliciesAboutDescriptionLink": "Další informace naleznete v dokumentaci.", + "defaultMappingsOptional": "Výchozí mapování (volitelné)", + "defaultMappingsOptionalDescription": "Výchozí mapování se používá, pokud nejsou pro organizaci definovány zásady organizace. Můžete zadat výchozí roli a mapování organizace pro návrat zde.", + "defaultMappingsRole": "Výchozí mapování rolí", + "defaultMappingsRoleDescription": "Výsledek tohoto výrazu musí vrátit název role definovaný v organizaci jako řetězec.", + "defaultMappingsOrg": "Výchozí mapování organizace", + "defaultMappingsOrgDescription": "Tento výraz musí vrátit org ID nebo pravdu, aby měl uživatel přístup k organizaci.", + "defaultMappingsSubmit": "Uložit výchozí mapování", + "orgPoliciesEdit": "Upravit zásady organizace", + "org": "Organizace", + "orgSelect": "Vyberte organizaci", + "orgSearch": "Hledat v org", + "orgNotFound": "Nenalezeny žádné org.", + "roleMappingPathOptional": "Cesta k mapování rolí (volitelné)", + "orgMappingPathOptional": "Cesta k mapování organizace (volitelné)", + "orgPolicyUpdate": "Aktualizovat přístupové právo", + "orgPolicyAdd": "Přidat přístupové právo", + "orgPolicyConfig": "Konfigurace přístupu pro organizaci", + "idpUpdatedDescription": "Poskytovatel identity byl úspěšně aktualizován", + "redirectUrl": "Přesměrovat URL", + "redirectUrlAbout": "O přesměrování URL", + "redirectUrlAboutDescription": "Toto je URL, na kterou budou uživatelé po ověření přesměrováni. Tuto URL je třeba nakonfigurovat v nastavení poskytovatele identity.", "pangolinAuth": "Auth - Pangolin", - "verificationCodeLengthRequirements": "Your verification code must be 8 characters.", - "errorOccurred": "An error occurred", - "emailErrorVerify": "Failed to verify email:", - "emailVerified": "Email successfully verified! Redirecting you...", - "verificationCodeErrorResend": "Failed to resend verification code:", - "verificationCodeResend": "Verification code resent", - "verificationCodeResendDescription": "We've resent a verification code to your email address. Please check your inbox.", - "emailVerify": "Verify Email", - "emailVerifyDescription": "Enter the verification code sent to your email address.", - "verificationCode": "Verification Code", - "verificationCodeEmailSent": "We sent a verification code to your email address.", - "submit": "Submit", - "emailVerifyResendProgress": "Resending...", - "emailVerifyResend": "Didn't receive a code? Click here to resend", - "passwordNotMatch": "Passwords do not match", - "signupError": "An error occurred while signing up", - "pangolinLogoAlt": "Pangolin Logo", - "inviteAlready": "Looks like you've been invited!", - "inviteAlreadyDescription": "To accept the invite, you must log in or create an account.", - "signupQuestion": "Already have an account?", - "login": "Log in", - "resourceNotFound": "Resource Not Found", - "resourceNotFoundDescription": "The resource you're trying to access does not exist.", - "pincodeRequirementsLength": "PIN must be exactly 6 digits", - "pincodeRequirementsChars": "PIN must only contain numbers", - "passwordRequirementsLength": "Password must be at least 1 character long", - "passwordRequirementsTitle": "Password requirements:", - "passwordRequirementLength": "At least 8 characters long", - "passwordRequirementUppercase": "At least one uppercase letter", - "passwordRequirementLowercase": "At least one lowercase letter", - "passwordRequirementNumber": "At least one number", - "passwordRequirementSpecial": "At least one special character", - "passwordRequirementsMet": "✓ Password meets all requirements", - "passwordStrength": "Password strength", - "passwordStrengthWeak": "Weak", - "passwordStrengthMedium": "Medium", - "passwordStrengthStrong": "Strong", - "passwordRequirements": "Requirements:", - "passwordRequirementLengthText": "8+ characters", - "passwordRequirementUppercaseText": "Uppercase letter (A-Z)", - "passwordRequirementLowercaseText": "Lowercase letter (a-z)", - "passwordRequirementNumberText": "Number (0-9)", + "verificationCodeLengthRequirements": "Váš ověřovací kód musí mít 8 znaků.", + "errorOccurred": "Došlo k chybě", + "emailErrorVerify": "Nepodařilo se ověřit e-mail:", + "emailVerified": "E-mail byl úspěšně ověřen! Přesměrování vás...", + "verificationCodeErrorResend": "Nepodařilo se znovu odeslat ověřovací kód:", + "verificationCodeResend": "Ověřovací kód znovu odeslán", + "verificationCodeResendDescription": "Znovu jsme odeslali ověřovací kód na vaši e-mailovou adresu. Zkontrolujte prosím svou doručenou poštu.", + "emailVerify": "Ověřit e-mail", + "emailVerifyDescription": "Zadejte ověřovací kód zaslaný na vaši e-mailovou adresu.", + "verificationCode": "Ověřovací kód", + "verificationCodeEmailSent": "Na vaši e-mailovou adresu jsme zaslali ověřovací kód.", + "submit": "Odeslat", + "emailVerifyResendProgress": "Znovu odesílání...", + "emailVerifyResend": "Neobdrželi jste kód? Klikněte zde pro opětovné odeslání", + "passwordNotMatch": "Hesla se neshodují", + "signupError": "Při registraci došlo k chybě", + "pangolinLogoAlt": "Logo Pangolin", + "inviteAlready": "Vypadá to, že jste byli pozváni!", + "inviteAlreadyDescription": "Chcete-li přijmout pozvánku, musíte se přihlásit nebo vytvořit účet.", + "signupQuestion": "Již máte účet?", + "login": "Přihlásit se", + "resourceNotFound": "Zdroj nebyl nalezen", + "resourceNotFoundDescription": "Dokument, ke kterému se snažíte přistupovat, neexistuje.", + "pincodeRequirementsLength": "PIN musí být přesně 6 číslic", + "pincodeRequirementsChars": "PIN musí obsahovat pouze čísla", + "passwordRequirementsLength": "Heslo musí mít alespoň 1 znak", + "passwordRequirementsTitle": "Požadavky na hesla:", + "passwordRequirementLength": "Nejméně 8 znaků dlouhé", + "passwordRequirementUppercase": "Alespoň jedno velké písmeno", + "passwordRequirementLowercase": "Alespoň jedno malé písmeno", + "passwordRequirementNumber": "Alespoň jedno číslo", + "passwordRequirementSpecial": "Alespoň jeden speciální znak", + "passwordRequirementsMet": "✓ Heslo splňuje všechny požadavky", + "passwordStrength": "Síla hesla", + "passwordStrengthWeak": "Slabé", + "passwordStrengthMedium": "Střední", + "passwordStrengthStrong": "Silný", + "passwordRequirements": "Požadavky:", + "passwordRequirementLengthText": "8+ znaků", + "passwordRequirementUppercaseText": "Velké písmeno (A-Z)", + "passwordRequirementLowercaseText": "Malé písmeno (a-z)", + "passwordRequirementNumberText": "Číslo (0-9)", "passwordRequirementSpecialText": "Special character (!@#$%...)", - "passwordsDoNotMatch": "Passwords do not match", - "otpEmailRequirementsLength": "OTP must be at least 1 character long", - "otpEmailSent": "OTP Sent", - "otpEmailSentDescription": "An OTP has been sent to your email", - "otpEmailErrorAuthenticate": "Failed to authenticate with email", - "pincodeErrorAuthenticate": "Failed to authenticate with pincode", - "passwordErrorAuthenticate": "Failed to authenticate with password", - "poweredBy": "Powered by", - "authenticationRequired": "Authentication Required", - "authenticationMethodChoose": "Choose your preferred method to access {name}", - "authenticationRequest": "You must authenticate to access {name}", - "user": "User", - "pincodeInput": "6-digit PIN Code", - "pincodeSubmit": "Log in with PIN", - "passwordSubmit": "Log In with Password", - "otpEmailDescription": "A one-time code will be sent to this email.", - "otpEmailSend": "Send One-time Code", - "otpEmail": "One-Time Password (OTP)", - "otpEmailSubmit": "Submit OTP", - "backToEmail": "Back to Email", - "noSupportKey": "Server is running without a supporter key. Consider supporting the project!", - "accessDenied": "Access Denied", - "accessDeniedDescription": "You're not allowed to access this resource. If this is a mistake, please contact the administrator.", - "accessTokenError": "Error checking access token", - "accessGranted": "Access Granted", - "accessUrlInvalid": "Access URL Invalid", - "accessGrantedDescription": "You have been granted access to this resource. Redirecting you...", - "accessUrlInvalidDescription": "This shared access URL is invalid. Please contact the resource owner for a new URL.", - "tokenInvalid": "Invalid token", - "pincodeInvalid": "Invalid code", - "passwordErrorRequestReset": "Failed to request reset:", - "passwordErrorReset": "Failed to reset password:", - "passwordResetSuccess": "Password reset successfully! Back to log in...", - "passwordReset": "Reset Password", - "passwordResetDescription": "Follow the steps to reset your password", - "passwordResetSent": "We'll send a password reset code to this email address.", + "passwordsDoNotMatch": "Hesla se neshodují", + "otpEmailRequirementsLength": "OTP musí mít alespoň 1 znak", + "otpEmailSent": "OTP odesláno", + "otpEmailSentDescription": "OTP bylo odesláno na váš e-mail", + "otpEmailErrorAuthenticate": "Ověření pomocí e-mailu se nezdařilo", + "pincodeErrorAuthenticate": "Ověření pomocí pincode se nezdařilo", + "passwordErrorAuthenticate": "Ověření pomocí hesla se nezdařilo", + "poweredBy": "Běží na", + "authenticationRequired": "Vyžadována autentizace", + "authenticationMethodChoose": "Vyberte si preferovanou metodu pro přístup k {name}", + "authenticationRequest": "Musíte se přihlásit k přístupu {name}", + "user": "Uživatel", + "pincodeInput": "6místný PIN kód", + "pincodeSubmit": "Přihlásit se pomocí PIN", + "passwordSubmit": "Přihlásit se pomocí hesla", + "otpEmailDescription": "Na tento e-mail bude zaslán jednorázový kód.", + "otpEmailSend": "Poslat jednorázový kód", + "otpEmail": "Jednorázové heslo (OTP)", + "otpEmailSubmit": "Odeslat OTP", + "backToEmail": "Zpět na e-mail", + "noSupportKey": "Server běží bez klíče podporovatele. Zvažte podporu projektu!", + "accessDenied": "Přístup odepřen", + "accessDeniedDescription": "Nemáte oprávnění k přístupu k tomuto dokumentu. Pokud se jedná o chybu, obraťte se na správce.", + "accessTokenError": "Chyba při kontrole přístupu token", + "accessGranted": "Přístup povolen", + "accessUrlInvalid": "Neplatná URL adresa přístupu", + "accessGrantedDescription": "Byl vám udělen přístup k tomuto zdroji. Přesměrování vás...", + "accessUrlInvalidDescription": "Tato URL adresa sdíleného přístupu je neplatná. Kontaktujte prosím vlastníka zdroje pro novou URL.", + "tokenInvalid": "Neplatný token", + "pincodeInvalid": "Neplatný kód", + "passwordErrorRequestReset": "Nepodařilo se požádat o reset:", + "passwordErrorReset": "Obnovení hesla selhalo:", + "passwordResetSuccess": "Obnovení hesla úspěšné! Zpět do přihlášení...", + "passwordReset": "Obnovit heslo", + "passwordResetDescription": "Postupujte podle kroků pro obnovení hesla", + "passwordResetSent": "Na tuto e-mailovou adresu zašleme kód pro obnovení hesla.", "passwordResetCode": "Reset Code", - "passwordResetCodeDescription": "Check your email for the reset code.", - "passwordNew": "New Password", - "passwordNewConfirm": "Confirm New Password", - "pincodeAuth": "Authenticator Code", - "pincodeSubmit2": "Submit Code", - "passwordResetSubmit": "Request Reset", - "passwordBack": "Back to Password", - "loginBack": "Go back to log in", - "signup": "Sign up", - "loginStart": "Log in to get started", - "idpOidcTokenValidating": "Validating OIDC token", - "idpOidcTokenResponse": "Validate OIDC token response", - "idpErrorOidcTokenValidating": "Error validating OIDC token", - "idpConnectingTo": "Connecting to {name}", - "idpConnectingToDescription": "Validating your identity", - "idpConnectingToProcess": "Connecting...", - "idpConnectingToFinished": "Connected", - "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", - "idpErrorNotFound": "IdP not found", + "passwordResetCodeDescription": "Zkontrolujte svůj e-mail pro kód pro obnovení.", + "passwordNew": "Nové heslo", + "passwordNewConfirm": "Potvrdit nové heslo", + "pincodeAuth": "Ověřovací kód", + "pincodeSubmit2": "Odeslat kód", + "passwordResetSubmit": "Žádost o obnovení", + "passwordBack": "Zpět na heslo", + "loginBack": "Přejít zpět na přihlášení", + "signup": "Zaregistrovat se", + "loginStart": "Přihlaste se a začněte", + "idpOidcTokenValidating": "Ověřování OIDC tokenu", + "idpOidcTokenResponse": "Potvrdit odpověď OIDC tokenu", + "idpErrorOidcTokenValidating": "Chyba při ověřování OIDC tokenu", + "idpConnectingTo": "Připojování k {name}", + "idpConnectingToDescription": "Ověření Vaší identity", + "idpConnectingToProcess": "Připojování...", + "idpConnectingToFinished": "Připojeno", + "idpErrorConnectingTo": "Při připojování k {name}došlo k chybě. Obraťte se na správce.", + "idpErrorNotFound": "IdP nenalezen", "idpGoogleAlt": "Google", "idpAzureAlt": "Azure", - "inviteInvalid": "Invalid Invite", - "inviteInvalidDescription": "The invite link is invalid.", - "inviteErrorWrongUser": "Invite is not for this user", - "inviteErrorUserNotExists": "User does not exist. Please create an account first.", - "inviteErrorLoginRequired": "You must be logged in to accept an invite", - "inviteErrorExpired": "The invite may have expired", - "inviteErrorRevoked": "The invite might have been revoked", - "inviteErrorTypo": "There could be a typo in the invite link", + "inviteInvalid": "Neplatná pozvánka", + "inviteInvalidDescription": "Odkaz pro pozvání je neplatný.", + "inviteErrorWrongUser": "Pozvat není pro tohoto uživatele", + "inviteErrorUserNotExists": "Uživatel neexistuje. Nejprve si vytvořte účet.", + "inviteErrorLoginRequired": "Musíte být přihlášeni, abyste mohli přijmout pozvánku", + "inviteErrorExpired": "Pozvánka možná vypršela", + "inviteErrorRevoked": "Pozvánka mohla být zrušena", + "inviteErrorTypo": "Na pozvánce může být typol", "pangolinSetup": "Setup - Pangolin", - "orgNameRequired": "Organization name is required", - "orgIdRequired": "Organization ID is required", - "orgErrorCreate": "An error occurred while creating org", - "pageNotFound": "Page Not Found", - "pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.", - "overview": "Overview", - "home": "Home", - "accessControl": "Access Control", - "settings": "Settings", - "usersAll": "All Users", - "license": "License", - "pangolinDashboard": "Dashboard - Pangolin", - "noResults": "No results found.", + "orgNameRequired": "Je vyžadován název organizace", + "orgIdRequired": "Je vyžadováno ID organizace", + "orgErrorCreate": "Při vytváření org došlo k chybě", + "pageNotFound": "Stránka nenalezena", + "pageNotFoundDescription": "Jejda! Stránka, kterou hledáte, neexistuje.", + "overview": "Přehled", + "home": "Domů", + "accessControl": "Kontrola přístupu", + "settings": "Nastavení", + "usersAll": "Všichni uživatelé", + "license": "Licence", + "pangolinDashboard": "Nástěnka - Pangolin", + "noResults": "Nebyly nalezeny žádné výsledky.", "terabytes": "{count} TB", "gigabytes": "{count} GB", "megabytes": "{count} MB", - "tagsEntered": "Entered Tags", - "tagsEnteredDescription": "These are the tags you`ve entered.", - "tagsWarnCannotBeLessThanZero": "maxTags and minTags cannot be less than 0", - "tagsWarnNotAllowedAutocompleteOptions": "Tag not allowed as per autocomplete options", - "tagsWarnInvalid": "Invalid tag as per validateTag", - "tagWarnTooShort": "Tag {tagText} is too short", - "tagWarnTooLong": "Tag {tagText} is too long", - "tagsWarnReachedMaxNumber": "Reached the maximum number of tags allowed", - "tagWarnDuplicate": "Duplicate tag {tagText} not added", - "supportKeyInvalid": "Invalid Key", - "supportKeyInvalidDescription": "Your supporter key is invalid.", + "tagsEntered": "Zadané štítky", + "tagsEnteredDescription": "Toto jsou značky, které jste zadali.", + "tagsWarnCannotBeLessThanZero": "maxTagy a minTagy nesmí být menší než 0", + "tagsWarnNotAllowedAutocompleteOptions": "Označení není povoleno podle možností automatického dokončování", + "tagsWarnInvalid": "Neplatný štítek pro ověření tagu", + "tagWarnTooShort": "Značka {tagText} je příliš krátká", + "tagWarnTooLong": "Značka {tagText} je příliš dlouhá", + "tagsWarnReachedMaxNumber": "Dosažen maximální povolený počet štítků", + "tagWarnDuplicate": "Duplicitní značka {tagText} nebyla přidána", + "supportKeyInvalid": "Neplatný klíč", + "supportKeyInvalidDescription": "Váš supporter klíč je neplatný.", "supportKeyValid": "Valid Key", - "supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!", - "supportKeyErrorValidationDescription": "Failed to validate supporter key.", - "supportKey": "Support Development and Adopt a Pangolin!", - "supportKeyDescription": "Purchase a supporter key to help us continue developing Pangolin for the community. Your contribution allows us to commit more time to maintain and add new features to the application for everyone. We will never use this to paywall features. This is separate from any Commercial Edition.", - "supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!", - "supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on", - "supportKeyPurchaseLink": "our website", - "supportKeyPurchase2": "and redeem it here.", - "supportKeyLearnMore": "Learn more.", - "supportKeyOptions": "Please select the option that best suits you.", - "supportKetOptionFull": "Full Supporter", - "forWholeServer": "For the whole server", - "lifetimePurchase": "Lifetime purchase", - "supporterStatus": "Supporter status", - "buy": "Buy", - "supportKeyOptionLimited": "Limited Supporter", - "forFiveUsers": "For 5 or less users", - "supportKeyRedeem": "Redeem Supporter Key", - "supportKeyHideSevenDays": "Hide for 7 days", - "supportKeyEnter": "Enter Supporter Key", - "supportKeyEnterDescription": "Meet your very own pet Pangolin!", + "supportKeyValidDescription": "Váš klíč podporovatele byl ověřen. Děkujeme za vaši podporu!", + "supportKeyErrorValidationDescription": "Nepodařilo se ověřit klíč podporovatele.", + "supportKey": "Podpořte vývoj a přijměte Pangolin!", + "supportKeyDescription": "Kupte klíč podporovatele, který nám pomůže pokračovat ve vývoji Pangolinu pro komunitu. Váš příspěvek nám umožňuje získat více času pro zachování a přidání nových funkcí do aplikace pro všechny. Nikdy to nepoužijeme pro funkce Paywall a to je oddělené od jakékoliv obchodní verze.", + "supportKeyPet": "Také se dostanete k adopci a setkáte se s vlastním mazlíčkem Pangolinem!", + "supportKeyPurchase": "Platby jsou zpracovávány přes GitHub. Poté můžete získat svůj klíč na", + "supportKeyPurchaseLink": "naše webové stránky", + "supportKeyPurchase2": "a znovu jej zde vyplatit.", + "supportKeyLearnMore": "Zjistěte více.", + "supportKeyOptions": "Vyberte si prosím možnost, která vám nejlépe vyhovuje.", + "supportKetOptionFull": "Plný podporovatel", + "forWholeServer": "Pro celý server", + "lifetimePurchase": "Doživotní nákup", + "supporterStatus": "Stav podporovatele", + "buy": "Koupit", + "supportKeyOptionLimited": "Omezený podporovatel", + "forFiveUsers": "Pro 5 nebo méně uživatelů", + "supportKeyRedeem": "Uplatnit klíč podpory", + "supportKeyHideSevenDays": "Skrýt na 7 dní", + "supportKeyEnter": "Zadejte klíč podpory", + "supportKeyEnterDescription": "Seznamte se s vlastním mazlíčkem Pangolin!", "githubUsername": "GitHub Username", - "supportKeyInput": "Supporter Key", - "supportKeyBuy": "Buy Supporter Key", - "logoutError": "Error logging out", - "signingAs": "Signed in as", - "serverAdmin": "Server Admin", - "managedSelfhosted": "Managed Self-Hosted", - "otpEnable": "Enable Two-factor", - "otpDisable": "Disable Two-factor", - "logout": "Log Out", - "licenseTierProfessionalRequired": "Professional Edition Required", - "licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.", - "actionGetOrg": "Get Organization", - "updateOrgUser": "Update Org User", - "createOrgUser": "Create Org User", - "actionUpdateOrg": "Update Organization", - "actionUpdateUser": "Update User", - "actionGetUser": "Get User", - "actionGetOrgUser": "Get Organization User", - "actionListOrgDomains": "List Organization Domains", - "actionCreateSite": "Create Site", - "actionDeleteSite": "Delete Site", - "actionGetSite": "Get Site", - "actionListSites": "List Sites", - "actionApplyBlueprint": "Apply Blueprint", - "setupToken": "Setup Token", - "setupTokenDescription": "Enter the setup token from the server console.", - "setupTokenRequired": "Setup token is required", - "actionUpdateSite": "Update Site", - "actionListSiteRoles": "List Allowed Site Roles", - "actionCreateResource": "Create Resource", - "actionDeleteResource": "Delete Resource", - "actionGetResource": "Get Resource", - "actionListResource": "List Resources", - "actionUpdateResource": "Update Resource", - "actionListResourceUsers": "List Resource Users", - "actionSetResourceUsers": "Set Resource Users", - "actionSetAllowedResourceRoles": "Set Allowed Resource Roles", - "actionListAllowedResourceRoles": "List Allowed Resource Roles", - "actionSetResourcePassword": "Set Resource Password", - "actionSetResourcePincode": "Set Resource Pincode", - "actionSetResourceEmailWhitelist": "Set Resource Email Whitelist", - "actionGetResourceEmailWhitelist": "Get Resource Email Whitelist", + "supportKeyInput": "Klíč pro podporu", + "supportKeyBuy": "Koupit klíč pro podporu", + "logoutError": "Chyba při odhlášení", + "signingAs": "Přihlášen jako", + "serverAdmin": "Správce serveru", + "managedSelfhosted": "Spravované vlastní hostování", + "otpEnable": "Povolit dvoufaktorové", + "otpDisable": "Zakázat dvoufaktorové", + "logout": "Odhlásit se", + "licenseTierProfessionalRequired": "Vyžadována profesionální edice", + "licenseTierProfessionalRequiredDescription": "Tato funkce je dostupná pouze v Professional Edition.", + "actionGetOrg": "Získat organizaci", + "updateOrgUser": "Aktualizovat Org uživatele", + "createOrgUser": "Vytvořit Org uživatele", + "actionUpdateOrg": "Aktualizovat organizaci", + "actionUpdateUser": "Aktualizovat uživatele", + "actionGetUser": "Získat uživatele", + "actionGetOrgUser": "Získat uživatele organizace", + "actionListOrgDomains": "Seznam domén organizace", + "actionCreateSite": "Vytvořit lokalitu", + "actionDeleteSite": "Odstranění lokality", + "actionGetSite": "Získat web", + "actionListSites": "Seznam stránek", + "actionApplyBlueprint": "Použít plán", + "setupToken": "Nastavit token", + "setupTokenDescription": "Zadejte nastavovací token z konzole serveru.", + "setupTokenRequired": "Je vyžadován token nastavení", + "actionUpdateSite": "Aktualizovat stránku", + "actionListSiteRoles": "Seznam povolených rolí webu", + "actionCreateResource": "Vytvořit zdroj", + "actionDeleteResource": "Odstranit dokument", + "actionGetResource": "Získat dokument", + "actionListResource": "Seznam zdrojů", + "actionUpdateResource": "Aktualizovat dokument", + "actionListResourceUsers": "Seznam uživatelů zdrojů", + "actionSetResourceUsers": "Nastavit uživatele zdrojů", + "actionSetAllowedResourceRoles": "Nastavit povolené role zdrojů", + "actionListAllowedResourceRoles": "Seznam povolených rolí zdrojů", + "actionSetResourcePassword": "Nastavit heslo zdroje", + "actionSetResourcePincode": "Nastavit zdrojový kód", + "actionSetResourceEmailWhitelist": "Nastavit seznam povolených dokumentů", + "actionGetResourceEmailWhitelist": "Získat seznam povolených dokumentů", "actionCreateTarget": "Create Target", - "actionDeleteTarget": "Delete Target", - "actionGetTarget": "Get Target", - "actionListTargets": "List Targets", + "actionDeleteTarget": "Odstranit cíl", + "actionGetTarget": "Získat cíl", + "actionListTargets": "Seznam cílů", "actionUpdateTarget": "Update Target", - "actionCreateRole": "Create Role", - "actionDeleteRole": "Delete Role", - "actionGetRole": "Get Role", - "actionListRole": "List Roles", - "actionUpdateRole": "Update Role", - "actionListAllowedRoleResources": "List Allowed Role Resources", - "actionInviteUser": "Invite User", - "actionRemoveUser": "Remove User", - "actionListUsers": "List Users", - "actionAddUserRole": "Add User Role", - "actionGenerateAccessToken": "Generate Access Token", - "actionDeleteAccessToken": "Delete Access Token", - "actionListAccessTokens": "List Access Tokens", - "actionCreateResourceRule": "Create Resource Rule", - "actionDeleteResourceRule": "Delete Resource Rule", - "actionListResourceRules": "List Resource Rules", - "actionUpdateResourceRule": "Update Resource Rule", - "actionListOrgs": "List Organizations", - "actionCheckOrgId": "Check ID", - "actionCreateOrg": "Create Organization", - "actionDeleteOrg": "Delete Organization", - "actionListApiKeys": "List API Keys", - "actionListApiKeyActions": "List API Key Actions", - "actionSetApiKeyActions": "Set API Key Allowed Actions", - "actionCreateApiKey": "Create API Key", - "actionDeleteApiKey": "Delete API Key", - "actionCreateIdp": "Create IDP", - "actionUpdateIdp": "Update IDP", - "actionDeleteIdp": "Delete IDP", - "actionListIdps": "List IDP", - "actionGetIdp": "Get IDP", - "actionCreateIdpOrg": "Create IDP Org Policy", - "actionDeleteIdpOrg": "Delete IDP Org Policy", - "actionListIdpOrgs": "List IDP Orgs", - "actionUpdateIdpOrg": "Update IDP Org", - "actionCreateClient": "Create Client", - "actionDeleteClient": "Delete Client", - "actionUpdateClient": "Update Client", - "actionListClients": "List Clients", - "actionGetClient": "Get Client", - "actionCreateSiteResource": "Create Site Resource", - "actionDeleteSiteResource": "Delete Site Resource", - "actionGetSiteResource": "Get Site Resource", - "actionListSiteResources": "List Site Resources", - "actionUpdateSiteResource": "Update Site Resource", - "actionListInvitations": "List Invitations", - "noneSelected": "None selected", - "orgNotFound2": "No organizations found.", - "searchProgress": "Search...", - "create": "Create", - "orgs": "Organizations", - "loginError": "An error occurred while logging in", - "passwordForgot": "Forgot your password?", - "otpAuth": "Two-Factor Authentication", - "otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.", - "otpAuthSubmit": "Submit Code", - "idpContinue": "Or continue with", - "otpAuthBack": "Back to Log In", + "actionCreateRole": "Vytvořit roli", + "actionDeleteRole": "Odstranit roli", + "actionGetRole": "Získat roli", + "actionListRole": "Seznam rolí", + "actionUpdateRole": "Aktualizovat roli", + "actionListAllowedRoleResources": "Seznam povolených zdrojů rolí", + "actionInviteUser": "Pozvat uživatele", + "actionRemoveUser": "Odstranit uživatele", + "actionListUsers": "Seznam uživatelů", + "actionAddUserRole": "Přidat uživatelskou roli", + "actionGenerateAccessToken": "Generovat přístupový token", + "actionDeleteAccessToken": "Odstranit přístupový token", + "actionListAccessTokens": "Seznam přístupových tokenů", + "actionCreateResourceRule": "Vytvořit pravidlo pro zdroj", + "actionDeleteResourceRule": "Odstranit pravidlo pro dokument", + "actionListResourceRules": "Seznam pravidel zdrojů", + "actionUpdateResourceRule": "Aktualizovat pravidlo pro dokument", + "actionListOrgs": "Seznam organizací", + "actionCheckOrgId": "ID kontroly", + "actionCreateOrg": "Vytvořit organizaci", + "actionDeleteOrg": "Odstranit organizaci", + "actionListApiKeys": "Seznam API klíčů", + "actionListApiKeyActions": "Seznam akcí API klíče", + "actionSetApiKeyActions": "Nastavit povolené akce API klíče", + "actionCreateApiKey": "Vytvořit klíč API", + "actionDeleteApiKey": "Odstranit klíč API", + "actionCreateIdp": "Vytvořit IDP", + "actionUpdateIdp": "Aktualizovat IDP", + "actionDeleteIdp": "Odstranit IDP", + "actionListIdps": "ID seznamu", + "actionGetIdp": "Získat IDP", + "actionCreateIdpOrg": "Vytvořit IDP Org Policy", + "actionDeleteIdpOrg": "Odstranit IDP Org Policy", + "actionListIdpOrgs": "Seznam IDP orgánů", + "actionUpdateIdpOrg": "Aktualizovat IDP Org", + "actionCreateClient": "Vytvořit klienta", + "actionDeleteClient": "Odstranit klienta", + "actionUpdateClient": "Aktualizovat klienta", + "actionListClients": "Seznam klientů", + "actionGetClient": "Získat klienta", + "actionCreateSiteResource": "Vytvořit zdroj webu", + "actionDeleteSiteResource": "Odstranit dokument webu", + "actionGetSiteResource": "Získat zdroj webu", + "actionListSiteResources": "Seznam zdrojů webu", + "actionUpdateSiteResource": "Aktualizovat dokument webu", + "actionListInvitations": "Seznam pozvánek", + "noneSelected": "Není vybráno", + "orgNotFound2": "Nebyly nalezeny žádné organizace.", + "searchProgress": "Hledat...", + "create": "Vytvořit", + "orgs": "Organizace", + "loginError": "Při přihlášení došlo k chybě", + "passwordForgot": "Zapomněli jste heslo?", + "otpAuth": "Dvoufaktorové ověření", + "otpAuthDescription": "Zadejte kód z vaší autentizační aplikace nebo jeden z vlastních záložních kódů.", + "otpAuthSubmit": "Odeslat kód", + "idpContinue": "Nebo pokračovat s", + "otpAuthBack": "Zpět na přihlášení", "navbar": "Navigation Menu", - "navbarDescription": "Main navigation menu for the application", - "navbarDocsLink": "Documentation", - "commercialEdition": "Commercial Edition", - "otpErrorEnable": "Unable to enable 2FA", - "otpErrorEnableDescription": "An error occurred while enabling 2FA", - "otpSetupCheckCode": "Please enter a 6-digit code", - "otpSetupCheckCodeRetry": "Invalid code. Please try again.", - "otpSetup": "Enable Two-factor Authentication", - "otpSetupDescription": "Secure your account with an extra layer of protection", - "otpSetupScanQr": "Scan this QR code with your authenticator app or enter the secret key manually:", - "otpSetupSecretCode": "Authenticator Code", - "otpSetupSuccess": "Two-Factor Authentication Enabled", - "otpSetupSuccessStoreBackupCodes": "Your account is now more secure. Don't forget to save your backup codes.", - "otpErrorDisable": "Unable to disable 2FA", - "otpErrorDisableDescription": "An error occurred while disabling 2FA", - "otpRemove": "Disable Two-factor Authentication", - "otpRemoveDescription": "Disable two-factor authentication for your account", - "otpRemoveSuccess": "Two-Factor Authentication Disabled", - "otpRemoveSuccessMessage": "Two-factor authentication has been disabled for your account. You can enable it again at any time.", - "otpRemoveSubmit": "Disable 2FA", - "paginator": "Page {current} of {last}", - "paginatorToFirst": "Go to first page", - "paginatorToPrevious": "Go to previous page", - "paginatorToNext": "Go to next page", - "paginatorToLast": "Go to last page", - "copyText": "Copy text", - "copyTextFailed": "Failed to copy text: ", - "copyTextClipboard": "Copy to clipboard", - "inviteErrorInvalidConfirmation": "Invalid confirmation", - "passwordRequired": "Password is required", - "allowAll": "Allow All", - "permissionsAllowAll": "Allow All Permissions", - "githubUsernameRequired": "GitHub username is required", - "supportKeyRequired": "Supporter key is required", - "passwordRequirementsChars": "Password must be at least 8 characters", - "language": "Language", - "verificationCodeRequired": "Code is required", - "userErrorNoUpdate": "No user to update", - "siteErrorNoUpdate": "No site to update", - "resourceErrorNoUpdate": "No resource to update", - "authErrorNoUpdate": "No auth info to update", - "orgErrorNoUpdate": "No org to update", - "orgErrorNoProvided": "No org provided", - "apiKeysErrorNoUpdate": "No API key to update", - "sidebarOverview": "Overview", - "sidebarHome": "Home", - "sidebarSites": "Sites", - "sidebarResources": "Resources", - "sidebarAccessControl": "Access Control", - "sidebarUsers": "Users", - "sidebarInvitations": "Invitations", - "sidebarRoles": "Roles", - "sidebarShareableLinks": "Shareable Links", - "sidebarApiKeys": "API Keys", - "sidebarSettings": "Settings", - "sidebarAllUsers": "All Users", - "sidebarIdentityProviders": "Identity Providers", - "sidebarLicense": "License", - "sidebarClients": "Clients (Beta)", - "sidebarDomains": "Domains", - "enableDockerSocket": "Enable Docker Blueprint", - "enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.", - "enableDockerSocketLink": "Learn More", - "viewDockerContainers": "View Docker Containers", - "containersIn": "Containers in {siteName}", - "selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.", - "containerName": "Name", - "containerImage": "Image", - "containerState": "State", - "containerNetworks": "Networks", + "navbarDescription": "Hlavní navigační menu aplikace", + "navbarDocsLink": "Dokumentace", + "commercialEdition": "Obchodní vydání", + "otpErrorEnable": "2FA nelze povolit", + "otpErrorEnableDescription": "Došlo k chybě při povolování 2FA", + "otpSetupCheckCode": "Zadejte 6místný kód", + "otpSetupCheckCodeRetry": "Neplatný kód. Zkuste to prosím znovu.", + "otpSetup": "Povolit dvoufaktorové ověření", + "otpSetupDescription": "Zabezpečte svůj účet s další vrstvou ochrany", + "otpSetupScanQr": "Naskenujte tento QR kód pomocí vaší autentizační aplikace nebo zadejte tajný klíč ručně:", + "otpSetupSecretCode": "Ověřovací kód", + "otpSetupSuccess": "Dvoufázové ověřování povoleno", + "otpSetupSuccessStoreBackupCodes": "Váš účet je nyní bezpečnější. Nezapomeňte uložit své záložní kódy.", + "otpErrorDisable": "2FA nelze zakázat", + "otpErrorDisableDescription": "Došlo k chybě při zakázání 2FA", + "otpRemove": "Zakázat dvoufaktorové ověřování", + "otpRemoveDescription": "Zakázat dvoufaktorové ověřování vašeho účtu", + "otpRemoveSuccess": "Dvoufázové ověřování zakázáno", + "otpRemoveSuccessMessage": "Dvoufaktorové ověřování bylo pro váš účet zakázáno. Můžete jej kdykoliv znovu povolit.", + "otpRemoveSubmit": "Zakázat 2FA", + "paginator": "Strana {current} z {last}", + "paginatorToFirst": "Přejít na první stránku", + "paginatorToPrevious": "Přejít na předchozí stránku", + "paginatorToNext": "Přejít na další stránku", + "paginatorToLast": "Přejít na poslední stránku", + "copyText": "Kopírovat text", + "copyTextFailed": "Nepodařilo se zkopírovat text: ", + "copyTextClipboard": "Kopírovat do schránky", + "inviteErrorInvalidConfirmation": "Neplatné potvrzení", + "passwordRequired": "Heslo je vyžadováno", + "allowAll": "Povolit vše", + "permissionsAllowAll": "Povolit všechna oprávnění", + "githubUsernameRequired": "Je vyžadováno uživatelské jméno GitHub", + "supportKeyRequired": "Je vyžadován klíč podpory", + "passwordRequirementsChars": "Heslo musí mít alespoň 8 znaků", + "language": "Jazyk", + "verificationCodeRequired": "Kód je povinný", + "userErrorNoUpdate": "Žádný uživatel k aktualizaci", + "siteErrorNoUpdate": "Žádná stránka k aktualizaci", + "resourceErrorNoUpdate": "Žádný zdroj k aktualizaci", + "authErrorNoUpdate": "Žádné informace o ověření k aktualizaci", + "orgErrorNoUpdate": "Žádný z orgů k aktualizaci", + "orgErrorNoProvided": "Není k dispozici žádný org", + "apiKeysErrorNoUpdate": "Žádný API klíč k aktualizaci", + "sidebarOverview": "Přehled", + "sidebarHome": "Domů", + "sidebarSites": "Stránky", + "sidebarResources": "Zdroje", + "sidebarAccessControl": "Kontrola přístupu", + "sidebarUsers": "Uživatelé", + "sidebarInvitations": "Pozvánky", + "sidebarRoles": "Role", + "sidebarShareableLinks": "Sdílené odkazy", + "sidebarApiKeys": "API klíče", + "sidebarSettings": "Nastavení", + "sidebarAllUsers": "Všichni uživatelé", + "sidebarIdentityProviders": "Poskytovatelé identity", + "sidebarLicense": "Licence", + "sidebarClients": "Klienti (Beta)", + "sidebarDomains": "Domény", + "enableDockerSocket": "Povolit Docker plán", + "enableDockerSocketDescription": "Povolte seškrábání štítků na Docker Socket pro popisky plánů. Nová cesta musí být k dispozici.", + "enableDockerSocketLink": "Zjistit více", + "viewDockerContainers": "Zobrazit kontejnery Dockeru", + "containersIn": "Kontejnery v {siteName}", + "selectContainerDescription": "Vyberte jakýkoli kontejner pro použití jako název hostitele pro tento cíl. Klikněte na port pro použití portu.", + "containerName": "Jméno", + "containerImage": "Obrázek", + "containerState": "Stát", + "containerNetworks": "Síť", "containerHostnameIp": "Hostname/IP", - "containerLabels": "Labels", - "containerLabelsCount": "{count, plural, one {# label} other {# labels}}", - "containerLabelsTitle": "Container Labels", + "containerLabels": "Popisky", + "containerLabelsCount": "{count, plural, one {# štítek} other {# štítků}}", + "containerLabelsTitle": "Popisky kontejneru", "containerLabelEmpty": "", - "containerPorts": "Ports", - "containerPortsMore": "+{count} more", - "containerActions": "Actions", - "select": "Select", - "noContainersMatchingFilters": "No containers found matching the current filters.", - "showContainersWithoutPorts": "Show containers without ports", - "showStoppedContainers": "Show stopped containers", - "noContainersFound": "No containers found. Make sure Docker containers are running.", - "searchContainersPlaceholder": "Search across {count} containers...", - "searchResultsCount": "{count, plural, one {# result} other {# results}}", - "filters": "Filters", - "filterOptions": "Filter Options", - "filterPorts": "Ports", - "filterStopped": "Stopped", - "clearAllFilters": "Clear all filters", - "columns": "Columns", - "toggleColumns": "Toggle Columns", - "refreshContainersList": "Refresh containers list", - "searching": "Searching...", - "noContainersFoundMatching": "No containers found matching \"{filter}\".", - "light": "light", - "dark": "dark", - "system": "system", - "theme": "Theme", - "subnetRequired": "Subnet is required", - "initialSetupTitle": "Initial Server Setup", - "initialSetupDescription": "Create the intial server admin account. Only one server admin can exist. You can always change these credentials later.", - "createAdminAccount": "Create Admin Account", - "setupErrorCreateAdmin": "An error occurred while creating the server admin account.", - "certificateStatus": "Certificate Status", - "loading": "Loading", - "restart": "Restart", - "domains": "Domains", - "domainsDescription": "Manage domains for your organization", - "domainsSearch": "Search domains...", - "domainAdd": "Add Domain", - "domainAddDescription": "Register a new domain with your organization", - "domainCreate": "Create Domain", - "domainCreatedDescription": "Domain created successfully", - "domainDeletedDescription": "Domain deleted successfully", - "domainQuestionRemove": "Are you sure you want to remove the domain {domain} from your account?", - "domainMessageRemove": "Once removed, the domain will no longer be associated with your account.", - "domainMessageConfirm": "To confirm, please type the domain name below.", - "domainConfirmDelete": "Confirm Delete Domain", - "domainDelete": "Delete Domain", - "domain": "Domain", - "selectDomainTypeNsName": "Domain Delegation (NS)", - "selectDomainTypeNsDescription": "This domain and all its subdomains. Use this when you want to control an entire domain zone.", - "selectDomainTypeCnameName": "Single Domain (CNAME)", - "selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.", - "selectDomainTypeWildcardName": "Wildcard Domain", - "selectDomainTypeWildcardDescription": "This domain and its subdomains.", - "domainDelegation": "Single Domain", - "selectType": "Select a type", - "actions": "Actions", - "refresh": "Refresh", - "refreshError": "Failed to refresh data", - "verified": "Verified", - "pending": "Pending", - "sidebarBilling": "Billing", - "billing": "Billing", - "orgBillingDescription": "Manage your billing information and subscriptions", + "containerPorts": "Přístavy", + "containerPortsMore": "+{count} další", + "containerActions": "Akce", + "select": "Vybrat", + "noContainersMatchingFilters": "Nebyly nalezeny žádné kontejnery odpovídající aktuálním filtrům.", + "showContainersWithoutPorts": "Zobrazit kontejnery bez portů", + "showStoppedContainers": "Zobrazit zastavené kontejnery", + "noContainersFound": "Nebyly nalezeny žádné kontejnery. Ujistěte se, že jsou v kontejnerech Docker spuštěny.", + "searchContainersPlaceholder": "Hledat napříč {count} kontejnery...", + "searchResultsCount": "{count, plural, one {# výsledek} other {# výsledků}}", + "filters": "Filtry", + "filterOptions": "Možnosti filtru", + "filterPorts": "Přístavy", + "filterStopped": "Zastaveno", + "clearAllFilters": "Vymazat všechny filtry", + "columns": "Sloupce", + "toggleColumns": "Přepnout sloupce", + "refreshContainersList": "Aktualizovat seznam kontejnerů", + "searching": "Vyhledávání...", + "noContainersFoundMatching": "Nebyly nalezeny žádné kontejnery odpovídající \"{filter}\".", + "light": "lehké", + "dark": "tmavé", + "system": "systém", + "theme": "Téma", + "subnetRequired": "Podsíť je vyžadována", + "initialSetupTitle": "Počáteční nastavení serveru", + "initialSetupDescription": "Vytvořte účet správce intial serveru. Pouze jeden správce serveru může existovat. Tyto přihlašovací údaje můžete kdykoliv změnit.", + "createAdminAccount": "Vytvořit účet správce", + "setupErrorCreateAdmin": "Došlo k chybě při vytváření účtu správce serveru.", + "certificateStatus": "Stav certifikátu", + "loading": "Načítání", + "restart": "Restartovat", + "domains": "Domény", + "domainsDescription": "Spravovat domény pro vaši organizaci", + "domainsSearch": "Hledat domény...", + "domainAdd": "Přidat doménu", + "domainAddDescription": "Registrujte novou doménu ve vaší organizaci", + "domainCreate": "Vytvořit doménu", + "domainCreatedDescription": "Doména byla úspěšně vytvořena", + "domainDeletedDescription": "Doména byla úspěšně odstraněna", + "domainQuestionRemove": "Opravdu chcete odstranit doménu {domain} z vašeho účtu?", + "domainMessageRemove": "Po odstranění domény již nebude přiřazena k vašemu účtu.", + "domainMessageConfirm": "Pro potvrzení zadejte název domény níže.", + "domainConfirmDelete": "Potvrdit odstranění domény", + "domainDelete": "Odstranit doménu", + "domain": "Doména", + "selectDomainTypeNsName": "Delegace domény (blíže neurčeno)", + "selectDomainTypeNsDescription": "Tato doména a všechny její subdomény. Použijte tuto možnost, pokud chcete ovládat celou doménu zóny.", + "selectDomainTypeCnameName": "Jedna doména (CNAME)", + "selectDomainTypeCnameDescription": "Jen tato konkrétní doména. Použijte tuto možnost pro jednotlivé subdomény nebo konkrétní doménové položky.", + "selectDomainTypeWildcardName": "Doména zástupného znaku", + "selectDomainTypeWildcardDescription": "Tato doména a její subdomény.", + "domainDelegation": "Jedna doména", + "selectType": "Vyberte typ", + "actions": "Akce", + "refresh": "Aktualizovat", + "refreshError": "Obnovení dat se nezdařilo", + "verified": "Ověřeno", + "pending": "Nevyřízeno", + "sidebarBilling": "Fakturace", + "billing": "Fakturace", + "orgBillingDescription": "Spravujte své fakturační údaje a předplatné", "github": "GitHub", - "pangolinHosted": "Pangolin Hosted", - "fossorial": "Fossorial", - "completeAccountSetup": "Complete Account Setup", - "completeAccountSetupDescription": "Set your password to get started", - "accountSetupSent": "We'll send an account setup code to this email address.", - "accountSetupCode": "Setup Code", - "accountSetupCodeDescription": "Check your email for the setup code.", - "passwordCreate": "Create Password", - "passwordCreateConfirm": "Confirm Password", - "accountSetupSubmit": "Send Setup Code", - "completeSetup": "Complete Setup", - "accountSetupSuccess": "Account setup completed! Welcome to Pangolin!", - "documentation": "Documentation", - "saveAllSettings": "Save All Settings", - "settingsUpdated": "Settings updated", - "settingsUpdatedDescription": "All settings have been updated successfully", - "settingsErrorUpdate": "Failed to update settings", - "settingsErrorUpdateDescription": "An error occurred while updating settings", - "sidebarCollapse": "Collapse", - "sidebarExpand": "Expand", - "newtUpdateAvailable": "Update Available", - "newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.", - "domainPickerEnterDomain": "Domain", + "pangolinHosted": "Pangolin hostovaný", + "fossorial": "Fossorální", + "completeAccountSetup": "Dokončit nastavení účtu", + "completeAccountSetupDescription": "Nastavte heslo pro spuštění", + "accountSetupSent": "Na tuto e-mailovou adresu zašleme nastavovací kód účtu.", + "accountSetupCode": "Nastavte kód", + "accountSetupCodeDescription": "Zkontrolujte svůj e-mail pro nastavení.", + "passwordCreate": "Vytvořit heslo", + "passwordCreateConfirm": "Potvrďte heslo", + "accountSetupSubmit": "Odeslat instalační kód", + "completeSetup": "Dokončit nastavení", + "accountSetupSuccess": "Nastavení účtu dokončeno! Vítejte v Pangolinu!", + "documentation": "Dokumentace", + "saveAllSettings": "Uložit všechna nastavení", + "settingsUpdated": "Nastavení aktualizováno", + "settingsUpdatedDescription": "Všechna nastavení byla úspěšně aktualizována", + "settingsErrorUpdate": "Aktualizace nastavení se nezdařila", + "settingsErrorUpdateDescription": "Došlo k chybě při aktualizaci nastavení", + "sidebarCollapse": "Sbalit", + "sidebarExpand": "Rozbalit", + "newtUpdateAvailable": "Dostupná aktualizace", + "newtUpdateAvailableInfo": "Je k dispozici nová verze Newt. Pro nejlepší zážitek prosím aktualizujte na nejnovější verzi.", + "domainPickerEnterDomain": "Doména", "domainPickerPlaceholder": "myapp.example.com", - "domainPickerDescription": "Enter the full domain of the resource to see available options.", - "domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options", - "domainPickerTabAll": "All", - "domainPickerTabOrganization": "Organization", - "domainPickerTabProvided": "Provided", + "domainPickerDescription": "Zadejte úplnou doménu zdroje pro zobrazení dostupných možností.", + "domainPickerDescriptionSaas": "Zadejte celou doménu, subdoménu nebo jméno pro zobrazení dostupných možností", + "domainPickerTabAll": "Vše", + "domainPickerTabOrganization": "Organizace", + "domainPickerTabProvided": "Poskytnuto", "domainPickerSortAsc": "A-Z", "domainPickerSortDesc": "Z-A", - "domainPickerCheckingAvailability": "Checking availability...", - "domainPickerNoMatchingDomains": "No matching domains found. Try a different domain or check your organization's domain settings.", - "domainPickerOrganizationDomains": "Organization Domains", - "domainPickerProvidedDomains": "Provided Domains", - "domainPickerSubdomain": "Subdomain: {subdomain}", - "domainPickerNamespace": "Namespace: {namespace}", - "domainPickerShowMore": "Show More", - "domainNotFound": "Domain Not Found", - "domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.", - "failed": "Failed", - "createNewOrgDescription": "Create a new organization", - "organization": "Organization", - "port": "Port", - "securityKeyManage": "Manage Security Keys", - "securityKeyDescription": "Add or remove security keys for passwordless authentication", - "securityKeyRegister": "Register New Security Key", - "securityKeyList": "Your Security Keys", - "securityKeyNone": "No security keys registered yet", - "securityKeyNameRequired": "Name is required", - "securityKeyRemove": "Remove", - "securityKeyLastUsed": "Last used: {date}", - "securityKeyNameLabel": "Security Key Name", - "securityKeyRegisterSuccess": "Security key registered successfully", - "securityKeyRegisterError": "Failed to register security key", - "securityKeyRemoveSuccess": "Security key removed successfully", - "securityKeyRemoveError": "Failed to remove security key", - "securityKeyLoadError": "Failed to load security keys", - "securityKeyLogin": "Continue with security key", - "securityKeyAuthError": "Failed to authenticate with security key", - "securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.", - "registering": "Registering...", - "securityKeyPrompt": "Please verify your identity using your security key. Make sure your security key is connected and ready.", - "securityKeyBrowserNotSupported": "Your browser doesn't support security keys. Please use a modern browser like Chrome, Firefox, or Safari.", - "securityKeyPermissionDenied": "Please allow access to your security key to continue signing in.", - "securityKeyRemovedTooQuickly": "Please keep your security key connected until the sign-in process completes.", - "securityKeyNotSupported": "Your security key may not be compatible. Please try a different security key.", - "securityKeyUnknownError": "There was a problem using your security key. Please try again.", - "twoFactorRequired": "Two-factor authentication is required to register a security key.", - "twoFactor": "Two-Factor Authentication", - "adminEnabled2FaOnYourAccount": "Your administrator has enabled two-factor authentication for {email}. Please complete the setup process to continue.", - "continueToApplication": "Continue to Application", - "securityKeyAdd": "Add Security Key", - "securityKeyRegisterTitle": "Register New Security Key", - "securityKeyRegisterDescription": "Connect your security key and enter a name to identify it", - "securityKeyTwoFactorRequired": "Two-Factor Authentication Required", - "securityKeyTwoFactorDescription": "Please enter your two-factor authentication code to register the security key", - "securityKeyTwoFactorRemoveDescription": "Please enter your two-factor authentication code to remove the security key", - "securityKeyTwoFactorCode": "Two-Factor Code", - "securityKeyRemoveTitle": "Remove Security Key", - "securityKeyRemoveDescription": "Enter your password to remove the security key \"{name}\"", - "securityKeyNoKeysRegistered": "No security keys registered", - "securityKeyNoKeysDescription": "Add a security key to enhance your account security", - "createDomainRequired": "Domain is required", - "createDomainAddDnsRecords": "Add DNS Records", - "createDomainAddDnsRecordsDescription": "Add the following DNS records to your domain provider to complete the setup.", - "createDomainNsRecords": "NS Records", - "createDomainRecord": "Record", - "createDomainType": "Type:", - "createDomainName": "Name:", - "createDomainValue": "Value:", - "createDomainCnameRecords": "CNAME Records", - "createDomainARecords": "A Records", - "createDomainRecordNumber": "Record {number}", - "createDomainTxtRecords": "TXT Records", - "createDomainSaveTheseRecords": "Save These Records", - "createDomainSaveTheseRecordsDescription": "Make sure to save these DNS records as you will not see them again.", - "createDomainDnsPropagation": "DNS Propagation", - "createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.", - "resourcePortRequired": "Port number is required for non-HTTP resources", - "resourcePortNotAllowed": "Port number should not be set for HTTP resources", + "domainPickerCheckingAvailability": "Kontrola dostupnosti...", + "domainPickerNoMatchingDomains": "Nebyly nalezeny žádné odpovídající domény. Zkuste jinou doménu nebo zkontrolujte nastavení domény vaší organizace.", + "domainPickerOrganizationDomains": "Domény organizace", + "domainPickerProvidedDomains": "Poskytnuté domény", + "domainPickerSubdomain": "Subdoména: {subdomain}", + "domainPickerNamespace": "Jmenný prostor: {namespace}", + "domainPickerShowMore": "Zobrazit více", + "domainNotFound": "Doména nenalezena", + "domainNotFoundDescription": "Tento dokument je zakázán, protože doména již neexistuje náš systém. Nastavte prosím novou doménu pro tento dokument.", + "failed": "Selhalo", + "createNewOrgDescription": "Vytvořit novou organizaci", + "organization": "Organizace", + "port": "Přístav", + "securityKeyManage": "Správa bezpečnostních klíčů", + "securityKeyDescription": "Přidat nebo odebrat bezpečnostní klíče pro bezheslou autentizaci", + "securityKeyRegister": "Registrovat nový bezpečnostní klíč", + "securityKeyList": "Vaše bezpečnostní klíče", + "securityKeyNone": "Zatím nejsou registrovány žádné bezpečnostní klíče", + "securityKeyNameRequired": "Název je povinný", + "securityKeyRemove": "Odebrat", + "securityKeyLastUsed": "Naposledy použito: {date}", + "securityKeyNameLabel": "Název bezpečnostního klíče", + "securityKeyRegisterSuccess": "Bezpečnostní klíč byl úspěšně zaregistrován", + "securityKeyRegisterError": "Nepodařilo se zaregistrovat bezpečnostní klíč", + "securityKeyRemoveSuccess": "Bezpečnostní klíč byl úspěšně odstraněn", + "securityKeyRemoveError": "Odstranění bezpečnostního klíče se nezdařilo", + "securityKeyLoadError": "Nepodařilo se načíst bezpečnostní klíče", + "securityKeyLogin": "Pokračovat s bezpečnostním klíčem", + "securityKeyAuthError": "Ověření bezpečnostním klíčem se nezdařilo", + "securityKeyRecommendation": "Registrujte záložní bezpečnostní klíč na jiném zařízení, abyste zajistili, že budete mít vždy přístup ke svému účtu.", + "registering": "Registrace...", + "securityKeyPrompt": "Ověřte svou identitu pomocí bezpečnostního klíče. Ujistěte se, že je Váš bezpečnostní klíč připojen a připraven.", + "securityKeyBrowserNotSupported": "Váš prohlížeč nepodporuje bezpečnostní klíče. Použijte prosím moderní prohlížeč jako Chrome, Firefox nebo Safari.", + "securityKeyPermissionDenied": "Prosím povolte přístup k bezpečnostnímu klíči, abyste mohli pokračovat v přihlašování.", + "securityKeyRemovedTooQuickly": "Prosím udržujte svůj bezpečnostní klíč připojený do dokončení procesu přihlášení.", + "securityKeyNotSupported": "Váš bezpečnostní klíč nemusí být kompatibilní. Zkuste jiný bezpečnostní klíč.", + "securityKeyUnknownError": "Vyskytl se problém s použitím bezpečnostního klíče. Zkuste to prosím znovu.", + "twoFactorRequired": "Pro registraci bezpečnostního klíče je nutné dvoufaktorové ověření.", + "twoFactor": "Dvoufaktorové ověření", + "adminEnabled2FaOnYourAccount": "Váš správce povolil dvoufaktorové ověřování pro {email}. Chcete-li pokračovat, dokončete proces nastavení.", + "continueToApplication": "Pokračovat do aplikace", + "securityKeyAdd": "Přidat bezpečnostní klíč", + "securityKeyRegisterTitle": "Registrovat nový bezpečnostní klíč", + "securityKeyRegisterDescription": "Připojte svůj bezpečnostní klíč a zadejte jméno pro jeho identifikaci", + "securityKeyTwoFactorRequired": "Vyžadováno dvoufaktorové ověření", + "securityKeyTwoFactorDescription": "Zadejte prosím váš dvoufaktorový ověřovací kód pro registraci bezpečnostního klíče", + "securityKeyTwoFactorRemoveDescription": "Zadejte prosím váš dvoufaktorový ověřovací kód pro odstranění bezpečnostního klíče", + "securityKeyTwoFactorCode": "Dvoufaktorový kód", + "securityKeyRemoveTitle": "Odstranit bezpečnostní klíč", + "securityKeyRemoveDescription": "Zadejte své heslo pro odstranění bezpečnostního klíče \"{name}\"", + "securityKeyNoKeysRegistered": "Nebyly registrovány žádné bezpečnostní klíče", + "securityKeyNoKeysDescription": "Přidejte bezpečnostní klíč pro zvýšení zabezpečení vašeho účtu", + "createDomainRequired": "Doména je vyžadována", + "createDomainAddDnsRecords": "Přidat DNS záznamy", + "createDomainAddDnsRecordsDescription": "Přidejte následující DNS záznamy k poskytovateli domény pro dokončení nastavení.", + "createDomainNsRecords": "NS záznamy", + "createDomainRecord": "Nahrávat", + "createDomainType": "Typ:", + "createDomainName": "Jméno:", + "createDomainValue": "Hodnota:", + "createDomainCnameRecords": "Záznamy CNAME", + "createDomainARecords": "Záznamy", + "createDomainRecordNumber": "Nahrát {number}", + "createDomainTxtRecords": "TXT záznamy", + "createDomainSaveTheseRecords": "Uložit tyto záznamy", + "createDomainSaveTheseRecordsDescription": "Ujistěte se, že chcete uložit tyto DNS záznamy, protože je znovu neuvidíte.", + "createDomainDnsPropagation": "Šíření DNS", + "createDomainDnsPropagationDescription": "Změna DNS může trvat nějakou dobu, než se šíří po internetu. To může trvat kdekoli od několika minut do 48 hodin v závislosti na poskytovateli DNS a nastavení TTL.", + "resourcePortRequired": "Pro neHTTP zdroje je vyžadováno číslo portu", + "resourcePortNotAllowed": "Číslo portu by nemělo být nastaveno pro HTTP zdroje", "signUpTerms": { - "IAgreeToThe": "I agree to the", - "termsOfService": "terms of service", - "and": "and", - "privacyPolicy": "privacy policy" + "IAgreeToThe": "Souhlasím s", + "termsOfService": "podmínky služby", + "and": "a", + "privacyPolicy": "zásady ochrany osobních údajů" }, - "siteRequired": "Site is required.", - "olmTunnel": "Olm Tunnel", - "olmTunnelDescription": "Use Olm for client connectivity", - "errorCreatingClient": "Error creating client", - "clientDefaultsNotFound": "Client defaults not found", - "createClient": "Create Client", - "createClientDescription": "Create a new client for connecting to your sites", - "seeAllClients": "See All Clients", - "clientInformation": "Client Information", - "clientNamePlaceholder": "Client name", - "address": "Address", - "subnetPlaceholder": "Subnet", - "addressDescription": "The address that this client will use for connectivity", - "selectSites": "Select sites", - "sitesDescription": "The client will have connectivity to the selected sites", - "clientInstallOlm": "Install Olm", - "clientInstallOlmDescription": "Get Olm running on your system", - "clientOlmCredentials": "Olm Credentials", - "clientOlmCredentialsDescription": "This is how Olm will authenticate with the server", - "olmEndpoint": "Olm Endpoint", + "siteRequired": "Stránka je povinná.", + "olmTunnel": "Starý tunel", + "olmTunnelDescription": "Použít Olm pro připojení klienta", + "errorCreatingClient": "Chyba při vytváření klienta", + "clientDefaultsNotFound": "Výchozí hodnoty klienta nebyly nalezeny", + "createClient": "Vytvořit klienta", + "createClientDescription": "Vytvořte nového klienta pro připojení k vašim stránkám", + "seeAllClients": "Zobrazit všechny klienty", + "clientInformation": "Informace o klientovi", + "clientNamePlaceholder": "Název klienta", + "address": "Adresa", + "subnetPlaceholder": "Podsíť", + "addressDescription": "Adresa, kterou bude tento klient používat pro připojení", + "selectSites": "Vyberte stránky", + "sitesDescription": "Klient bude mít připojení k vybraným webům", + "clientInstallOlm": "Nainstalovat Olm", + "clientInstallOlmDescription": "Stáhněte si Olm běžící ve vašem systému", + "clientOlmCredentials": "Olm pověření", + "clientOlmCredentialsDescription": "Tímto způsobem se bude Olm autentizovat se serverem", + "olmEndpoint": "Olm koncový bod", "olmId": "Olm ID", - "olmSecretKey": "Olm Secret Key", - "clientCredentialsSave": "Save Your Credentials", - "clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", - "generalSettingsDescription": "Configure the general settings for this client", - "clientUpdated": "Client updated", - "clientUpdatedDescription": "The client has been updated.", - "clientUpdateFailed": "Failed to update client", - "clientUpdateError": "An error occurred while updating the client.", - "sitesFetchFailed": "Failed to fetch sites", - "sitesFetchError": "An error occurred while fetching sites.", - "olmErrorFetchReleases": "An error occurred while fetching Olm releases.", - "olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.", - "remoteSubnets": "Remote Subnets", - "enterCidrRange": "Enter CIDR range", - "remoteSubnetsDescription": "Add CIDR ranges that can be accessed from this site remotely using clients. Use format like 10.0.0.0/24. This ONLY applies to VPN client connectivity.", - "resourceEnableProxy": "Enable Public Proxy", - "resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.", - "externalProxyEnabled": "External Proxy Enabled", + "olmSecretKey": "Olm tajný klíč", + "clientCredentialsSave": "Uložit přihlašovací údaje", + "clientCredentialsSaveDescription": "Toto nastavení uvidíte pouze jednou. Ujistěte se, že jej zkopírujete na bezpečné místo.", + "generalSettingsDescription": "Konfigurace obecných nastavení pro tohoto klienta", + "clientUpdated": "Klient byl aktualizován", + "clientUpdatedDescription": "Klient byl aktualizován.", + "clientUpdateFailed": "Nepodařilo se aktualizovat klienta", + "clientUpdateError": "Došlo k chybě při aktualizaci klienta.", + "sitesFetchFailed": "Nepodařilo se načíst stránky", + "sitesFetchError": "Došlo k chybě při načítání stránek.", + "olmErrorFetchReleases": "Při načítání vydání Olm došlo k chybě.", + "olmErrorFetchLatest": "Došlo k chybě při načítání nejnovější verze Olm.", + "remoteSubnets": "Vzdálené podsítě", + "enterCidrRange": "Zadejte rozsah CIDR", + "remoteSubnetsDescription": "Přidejte CIDR rozsahy, které mohou být přístupné z tohoto webu dálkově pomocí klientů. Použijte formát jako 10.0.0.0/24. Toto platí pro připojení klienta VPN.", + "resourceEnableProxy": "Povolit veřejné proxy", + "resourceEnableProxyDescription": "Povolit veřejné proxying pro tento zdroj. To umožňuje přístup ke zdrojům mimo síť prostřednictvím cloudu na otevřeném portu. Vyžaduje nastavení Traefik.", + "externalProxyEnabled": "Externí proxy povolen", "addNewTarget": "Add New Target", - "targetsList": "Targets List", - "targetErrorDuplicateTargetFound": "Duplicate target found", - "httpMethod": "HTTP Method", - "selectHttpMethod": "Select HTTP method", - "domainPickerSubdomainLabel": "Subdomain", - "domainPickerBaseDomainLabel": "Base Domain", - "domainPickerSearchDomains": "Search domains...", - "domainPickerNoDomainsFound": "No domains found", - "domainPickerLoadingDomains": "Loading domains...", - "domainPickerSelectBaseDomain": "Select base domain...", - "domainPickerNotAvailableForCname": "Not available for CNAME domains", - "domainPickerEnterSubdomainOrLeaveBlank": "Enter subdomain or leave blank to use base domain.", - "domainPickerEnterSubdomainToSearch": "Enter a subdomain to search and select from available free domains.", - "domainPickerFreeDomains": "Free Domains", - "domainPickerSearchForAvailableDomains": "Search for available domains", - "resourceDomain": "Domain", - "resourceEditDomain": "Edit Domain", - "siteName": "Site Name", - "proxyPort": "Port", - "resourcesTableProxyResources": "Proxy Resources", - "resourcesTableClientResources": "Client Resources", - "resourcesTableNoProxyResourcesFound": "No proxy resources found.", - "resourcesTableNoInternalResourcesFound": "No internal resources found.", - "resourcesTableDestination": "Destination", - "resourcesTableTheseResourcesForUseWith": "These resources are for use with", - "resourcesTableClients": "Clients", - "resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.", - "editInternalResourceDialogEditClientResource": "Edit Client Resource", - "editInternalResourceDialogUpdateResourceProperties": "Update the resource properties and target configuration for {resourceName}.", - "editInternalResourceDialogResourceProperties": "Resource Properties", - "editInternalResourceDialogName": "Name", - "editInternalResourceDialogProtocol": "Protocol", - "editInternalResourceDialogSitePort": "Site Port", + "targetsList": "Seznam cílů", + "targetErrorDuplicateTargetFound": "Byl nalezen duplicitní cíl", + "httpMethod": "HTTP metoda", + "selectHttpMethod": "Vyberte HTTP metodu", + "domainPickerSubdomainLabel": "Subdoména", + "domainPickerBaseDomainLabel": "Základní doména", + "domainPickerSearchDomains": "Hledat domény...", + "domainPickerNoDomainsFound": "Nebyly nalezeny žádné domény", + "domainPickerLoadingDomains": "Načítám domény...", + "domainPickerSelectBaseDomain": "Vyberte základní doménu...", + "domainPickerNotAvailableForCname": "Není k dispozici pro domény CNAME", + "domainPickerEnterSubdomainOrLeaveBlank": "Zadejte subdoménu nebo ponechte prázdné pro použití základní domény.", + "domainPickerEnterSubdomainToSearch": "Zadejte subdoménu pro hledání a výběr z dostupných domén zdarma.", + "domainPickerFreeDomains": "Volné domény", + "domainPickerSearchForAvailableDomains": "Hledat dostupné domény", + "resourceDomain": "Doména", + "resourceEditDomain": "Upravit doménu", + "siteName": "Název webu", + "proxyPort": "Přístav", + "resourcesTableProxyResources": "Zdroje proxy", + "resourcesTableClientResources": "Zdroje klienta", + "resourcesTableNoProxyResourcesFound": "Nebyly nalezeny žádné zdroje proxy", + "resourcesTableNoInternalResourcesFound": "Nebyly nalezeny žádné vnitřní zdroje.", + "resourcesTableDestination": "Místo určení", + "resourcesTableTheseResourcesForUseWith": "Tyto zdroje jsou určeny pro použití s", + "resourcesTableClients": "Klienti", + "resourcesTableAndOnlyAccessibleInternally": "a jsou interně přístupné pouze v případě, že jsou propojeni s klientem.", + "editInternalResourceDialogEditClientResource": "Upravit klientský dokument", + "editInternalResourceDialogUpdateResourceProperties": "Aktualizujte vlastnosti zdroje a cílovou konfiguraci pro {resourceName}.", + "editInternalResourceDialogResourceProperties": "Vlastnosti zdroje", + "editInternalResourceDialogName": "Jméno", + "editInternalResourceDialogProtocol": "Protokol", + "editInternalResourceDialogSitePort": "Port webu", "editInternalResourceDialogTargetConfiguration": "Target Configuration", - "editInternalResourceDialogCancel": "Cancel", - "editInternalResourceDialogSaveResource": "Save Resource", - "editInternalResourceDialogSuccess": "Success", - "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Internal resource updated successfully", - "editInternalResourceDialogError": "Error", - "editInternalResourceDialogFailedToUpdateInternalResource": "Failed to update internal resource", - "editInternalResourceDialogNameRequired": "Name is required", - "editInternalResourceDialogNameMaxLength": "Name must be less than 255 characters", - "editInternalResourceDialogProxyPortMin": "Proxy port must be at least 1", - "editInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536", - "editInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format", - "editInternalResourceDialogDestinationPortMin": "Destination port must be at least 1", - "editInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536", - "createInternalResourceDialogNoSitesAvailable": "No Sites Available", - "createInternalResourceDialogNoSitesAvailableDescription": "You need to have at least one Newt site with a subnet configured to create internal resources.", - "createInternalResourceDialogClose": "Close", - "createInternalResourceDialogCreateClientResource": "Create Client Resource", - "createInternalResourceDialogCreateClientResourceDescription": "Create a new resource that will be accessible to clients connected to the selected site.", - "createInternalResourceDialogResourceProperties": "Resource Properties", - "createInternalResourceDialogName": "Name", - "createInternalResourceDialogSite": "Site", - "createInternalResourceDialogSelectSite": "Select site...", - "createInternalResourceDialogSearchSites": "Search sites...", - "createInternalResourceDialogNoSitesFound": "No sites found.", - "createInternalResourceDialogProtocol": "Protocol", + "editInternalResourceDialogCancel": "Zrušit", + "editInternalResourceDialogSaveResource": "Uložit dokument", + "editInternalResourceDialogSuccess": "Úspěšně", + "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Interní zdroj byl úspěšně aktualizován", + "editInternalResourceDialogError": "Chyba", + "editInternalResourceDialogFailedToUpdateInternalResource": "Aktualizace interního zdroje se nezdařila", + "editInternalResourceDialogNameRequired": "Název je povinný", + "editInternalResourceDialogNameMaxLength": "Název musí mít méně než 255 znaků", + "editInternalResourceDialogProxyPortMin": "Port proxy serveru musí být alespoň 1", + "editInternalResourceDialogProxyPortMax": "Port proxy serveru musí být menší než 65536", + "editInternalResourceDialogInvalidIPAddressFormat": "Neplatný formát IP adresy", + "editInternalResourceDialogDestinationPortMin": "Cílový přístav musí být alespoň 1", + "editInternalResourceDialogDestinationPortMax": "Cílový přístav musí být nižší než 65536", + "createInternalResourceDialogNoSitesAvailable": "Nejsou k dispozici žádné weby", + "createInternalResourceDialogNoSitesAvailableDescription": "Musíte mít alespoň jeden Newt web s podsítí nakonfigurovanou pro vytvoření vnitřních zdrojů.", + "createInternalResourceDialogClose": "Zavřít", + "createInternalResourceDialogCreateClientResource": "Vytvořit klientský dokument", + "createInternalResourceDialogCreateClientResourceDescription": "Vytvořte nový zdroj, který bude přístupný klientům připojeným k vybranému webu.", + "createInternalResourceDialogResourceProperties": "Vlastnosti zdroje", + "createInternalResourceDialogName": "Jméno", + "createInternalResourceDialogSite": "Lokalita", + "createInternalResourceDialogSelectSite": "Vybrat lokalitu...", + "createInternalResourceDialogSearchSites": "Hledat lokality...", + "createInternalResourceDialogNoSitesFound": "Nebyly nalezeny žádné stránky.", + "createInternalResourceDialogProtocol": "Protokol", "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", - "createInternalResourceDialogSitePort": "Site Port", - "createInternalResourceDialogSitePortDescription": "Use this port to access the resource on the site when connected with a client.", + "createInternalResourceDialogSitePort": "Port webu", + "createInternalResourceDialogSitePortDescription": "Použijte tento port pro přístup ke zdroji na webu při připojení s klientem.", "createInternalResourceDialogTargetConfiguration": "Target Configuration", - "createInternalResourceDialogDestinationIPDescription": "The IP or hostname address of the resource on the site's network.", - "createInternalResourceDialogDestinationPortDescription": "The port on the destination IP where the resource is accessible.", - "createInternalResourceDialogCancel": "Cancel", - "createInternalResourceDialogCreateResource": "Create Resource", - "createInternalResourceDialogSuccess": "Success", - "createInternalResourceDialogInternalResourceCreatedSuccessfully": "Internal resource created successfully", - "createInternalResourceDialogError": "Error", - "createInternalResourceDialogFailedToCreateInternalResource": "Failed to create internal resource", - "createInternalResourceDialogNameRequired": "Name is required", - "createInternalResourceDialogNameMaxLength": "Name must be less than 255 characters", - "createInternalResourceDialogPleaseSelectSite": "Please select a site", - "createInternalResourceDialogProxyPortMin": "Proxy port must be at least 1", - "createInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536", - "createInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format", - "createInternalResourceDialogDestinationPortMin": "Destination port must be at least 1", - "createInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536", - "siteConfiguration": "Configuration", - "siteAcceptClientConnections": "Accept Client Connections", - "siteAcceptClientConnectionsDescription": "Allow other devices to connect through this Newt instance as a gateway using clients.", - "siteAddress": "Site Address", - "siteAddressDescription": "Specify the IP address of the host for clients to connect to. This is the internal address of the site in the Pangolin network for clients to address. Must fall within the Org subnet.", - "autoLoginExternalIdp": "Auto Login with External IDP", - "autoLoginExternalIdpDescription": "Immediately redirect the user to the external IDP for authentication.", - "selectIdp": "Select IDP", - "selectIdpPlaceholder": "Choose an IDP...", - "selectIdpRequired": "Please select an IDP when auto login is enabled.", - "autoLoginTitle": "Redirecting", - "autoLoginDescription": "Redirecting you to the external identity provider for authentication.", - "autoLoginProcessing": "Preparing authentication...", - "autoLoginRedirecting": "Redirecting to login...", - "autoLoginError": "Auto Login Error", - "autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.", - "autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.", + "createInternalResourceDialogDestinationIPDescription": "IP nebo název hostitele zdroje v síti webu.", + "createInternalResourceDialogDestinationPortDescription": "Přístav na cílové IP adrese, kde je zdroj dostupný.", + "createInternalResourceDialogCancel": "Zrušit", + "createInternalResourceDialogCreateResource": "Vytvořit zdroj", + "createInternalResourceDialogSuccess": "Úspěšně", + "createInternalResourceDialogInternalResourceCreatedSuccessfully": "Interní zdroj byl úspěšně vytvořen", + "createInternalResourceDialogError": "Chyba", + "createInternalResourceDialogFailedToCreateInternalResource": "Nepodařilo se vytvořit interní zdroj", + "createInternalResourceDialogNameRequired": "Název je povinný", + "createInternalResourceDialogNameMaxLength": "Název musí mít méně než 255 znaků", + "createInternalResourceDialogPleaseSelectSite": "Vyberte prosím web", + "createInternalResourceDialogProxyPortMin": "Port proxy serveru musí být alespoň 1", + "createInternalResourceDialogProxyPortMax": "Port proxy serveru musí být menší než 65536", + "createInternalResourceDialogInvalidIPAddressFormat": "Neplatný formát IP adresy", + "createInternalResourceDialogDestinationPortMin": "Cílový přístav musí být alespoň 1", + "createInternalResourceDialogDestinationPortMax": "Cílový přístav musí být nižší než 65536", + "siteConfiguration": "Konfigurace", + "siteAcceptClientConnections": "Přijmout připojení klienta", + "siteAcceptClientConnectionsDescription": "Umožnit ostatním zařízením připojit se prostřednictvím této instance Newt jako brána pomocí klientů.", + "siteAddress": "Adresa webu", + "siteAddressDescription": "Zadejte IP adresu hostitele pro připojení. Toto je interní adresa webu v síti Pangolin pro klienty. Musí spadat do podsítě Org.", + "autoLoginExternalIdp": "Automatické přihlášení pomocí externího IDP", + "autoLoginExternalIdpDescription": "Okamžitě přesměrujte uživatele na externí IDP k ověření.", + "selectIdp": "Vybrat IDP", + "selectIdpPlaceholder": "Vyberte IDP...", + "selectIdpRequired": "Prosím vyberte IDP, když je povoleno automatické přihlášení.", + "autoLoginTitle": "Přesměrování", + "autoLoginDescription": "Přesměrování k externímu poskytovateli identity pro ověření.", + "autoLoginProcessing": "Příprava ověřování...", + "autoLoginRedirecting": "Přesměrování k přihlášení...", + "autoLoginError": "Automatická chyba přihlášení", + "autoLoginErrorNoRedirectUrl": "Od poskytovatele identity nebyla obdržena žádná adresa URL.", + "autoLoginErrorGeneratingUrl": "Nepodařilo se vygenerovat ověřovací URL.", "managedSelfHosted": { - "title": "Managed Self-Hosted", - "description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles", - "introTitle": "Managed Self-Hosted Pangolin", - "introDescription": "is a deployment option designed for people who want simplicity and extra reliability while still keeping their data private and self-hosted.", - "introDetail": "With this option, you still run your own Pangolin node — your tunnels, SSL termination, and traffic all stay on your server. The difference is that management and monitoring are handled through our cloud dashboard, which unlocks a number of benefits:", + "title": "Spravované vlastní hostování", + "description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami", + "introTitle": "Spravovaný Pangolin", + "introDescription": "je možnost nasazení určená pro lidi, kteří chtějí jednoduchost a spolehlivost při zachování soukromých a samoobslužných dat.", + "introDetail": "Pomocí této volby stále provozujete vlastní uzel Pangolin — tunely, SSL terminály a provoz všech pobytů na vašem serveru. Rozdíl spočívá v tom, že řízení a monitorování se řeší prostřednictvím našeho cloudového panelu, který odemkne řadu výhod:", "benefitSimplerOperations": { - "title": "Simpler operations", - "description": "No need to run your own mail server or set up complex alerting. You'll get health checks and downtime alerts out of the box." + "title": "Jednoduchý provoz", + "description": "Není třeba spouštět svůj vlastní poštovní server nebo nastavit komplexní upozornění. Ze schránky dostanete upozornění na zdravotní kontrolu a výpadek." }, "benefitAutomaticUpdates": { - "title": "Automatic updates", - "description": "The cloud dashboard evolves quickly, so you get new features and bug fixes without having to manually pull new containers every time." + "title": "Automatické aktualizace", + "description": "Nástěnka cloudu se rychle vyvíjí, takže dostanete nové funkce a opravy chyb, aniž byste museli vždy ručně stahovat nové kontejnery." }, "benefitLessMaintenance": { - "title": "Less maintenance", - "description": "No database migrations, backups, or extra infrastructure to manage. We handle that in the cloud." + "title": "Méně údržby", + "description": "Žádná migrace do databáze, zálohování nebo další infrastruktura pro správu. Zabýváme se tím v cloudu." }, "benefitCloudFailover": { - "title": "Cloud failover", - "description": "If your node goes down, your tunnels can temporarily fail over to our cloud points of presence until you bring it back online." + "title": "Selhání cloudu", + "description": "Pokud váš uzel klesne, vaše tunely mohou dočasně selhat na naše body přítomnosti v cloudu, dokud jej nevrátíte zpět online." }, "benefitHighAvailability": { - "title": "High availability (PoPs)", - "description": "You can also attach multiple nodes to your account for redundancy and better performance." + "title": "Vysoká dostupnost (PoP)", + "description": "Můžete také připojit více uzlů k vašemu účtu pro nadbytečnost a lepší výkon." }, "benefitFutureEnhancements": { - "title": "Future enhancements", - "description": "We're planning to add more analytics, alerting, and management tools to make your deployment even more robust." + "title": "Budoucí vylepšení", + "description": "Plánujeme přidat více analytických, varovných a manažerských nástrojů, aby bylo vaše nasazení ještě robustnější." }, "docsAlert": { - "text": "Learn more about the Managed Self-Hosted option in our", - "documentation": "documentation" + "text": "Další informace o možnostech Managed Self-Hosted v našem", + "documentation": "dokumentace" }, - "convertButton": "Convert This Node to Managed Self-Hosted" + "convertButton": "Převést tento uzel na spravovaný vlastní hostitel" }, - "internationaldomaindetected": "International Domain Detected", - "willbestoredas": "Will be stored as:", - "idpGoogleDescription": "Google OAuth2/OIDC provider", + "internationaldomaindetected": "Zjištěna mezinárodní doména", + "willbestoredas": "Bude uloženo jako:", + "idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Custom Headers", - "headersValidationError": "Headers must be in the format: Header-Name: value.", - "domainPickerProvidedDomain": "Provided Domain", - "domainPickerFreeProvidedDomain": "Free Provided Domain", - "domainPickerVerified": "Verified", - "domainPickerUnverified": "Unverified", - "domainPickerInvalidSubdomainStructure": "This subdomain contains invalid characters or structure. It will be sanitized automatically when you save.", - "domainPickerError": "Error", - "domainPickerErrorLoadDomains": "Failed to load organization domains", - "domainPickerErrorCheckAvailability": "Failed to check domain availability", - "domainPickerInvalidSubdomain": "Invalid subdomain", - "domainPickerInvalidSubdomainRemoved": "The input \"{sub}\" was removed because it's not valid.", - "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" could not be made valid for {domain}.", - "domainPickerSubdomainSanitized": "Subdomain sanitized", - "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", - "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", - "resourceExposePortsEditFile": "Edit file: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "customHeaders": "Vlastní záhlaví", + "headersValidationError": "Záhlaví musí být ve formátu: název záhlaví: hodnota.", + "domainPickerProvidedDomain": "Poskytnutá doména", + "domainPickerFreeProvidedDomain": "Zdarma poskytnutá doména", + "domainPickerVerified": "Ověřeno", + "domainPickerUnverified": "Neověřeno", + "domainPickerInvalidSubdomainStructure": "Tato subdoména obsahuje neplatné znaky nebo strukturu. Bude automaticky sanitována při uložení.", + "domainPickerError": "Chyba", + "domainPickerErrorLoadDomains": "Nepodařilo se načíst domény organizace", + "domainPickerErrorCheckAvailability": "Kontrola dostupnosti domény se nezdařila", + "domainPickerInvalidSubdomain": "Neplatná subdoména", + "domainPickerInvalidSubdomainRemoved": "Vstup \"{sub}\" byl odstraněn, protože není platný.", + "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nemohl být platný pro {domain}.", + "domainPickerSubdomainSanitized": "Upravená subdoména", + "domainPickerSubdomainCorrected": "\"{sub}\" bylo opraveno na \"{sanitized}\"", + "resourceAddEntrypointsEditFile": "Upravit soubor: config/traefik/traefik_config.yml", + "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", + "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", + "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde." } From 26b2233168635871d5bd12aee5776a5b6e89dfc5 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:54 -0700 Subject: [PATCH 030/322] New translations en-us.json (German) --- messages/de-DE.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 57ec1bd3..98c0226c 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", + "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück." } From d209c8af9d79408b76d9db1089c7dd08e84d4e4e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:55 -0700 Subject: [PATCH 031/322] New translations en-us.json (Italian) --- messages/it-IT.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 57e9514f..ec74a974 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", + "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui." } From f3e8677ae4897a8bb95842e5d0e98f33ca74a8ef Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:56 -0700 Subject: [PATCH 032/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index f73190cf..91fd5aa8 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다", "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", + "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요." } From 2981e35c7537f59d7f000364f21de55de0668b21 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:57 -0700 Subject: [PATCH 033/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index b0a67894..da05681d 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", + "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug." } From ca7f1e5db8700b5f24658afa6d0e1cf3bcb5d7e5 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:29:58 -0700 Subject: [PATCH 034/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index fb2d021d..7ff49860 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", + "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj." } From 1ad5eb010aba5393cf5d250bd7ca1694e771fd02 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:00 -0700 Subject: [PATCH 035/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 72db4444..914a7dd3 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" foi corrigido para \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", + "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui." } From e360a5323d3aa84a2036c185ad5b4571e7ef9a2c Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:01 -0700 Subject: [PATCH 036/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index a7758452..94e93594 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", + "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда." } From 8504fd8d9dbacac4fde689f0a4e526d026189f40 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:02 -0700 Subject: [PATCH 037/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 47821f97..cf1c9157 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi", "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", + "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün." } From 248debb7c491e3501e7e6b189aaa96cb0a87edeb Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:04 -0700 Subject: [PATCH 038/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index fc85369e..656096e9 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"", "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", + "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。" } From 5fee1c3ebd7da8a899af5c18372f9c0ffb106c39 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:05 -0700 Subject: [PATCH 039/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index b9439f68..aca7fe6d 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" var korrigert til \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", + "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her." } From 558f302342186d98bf4de5e7f032204106ed862d Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 26 Sep 2025 10:30:06 -0700 Subject: [PATCH 040/322] New translations en-us.json (French) --- messages/fr-FR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 8aa3f192..2b431ccf 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1520,6 +1520,6 @@ "domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"", "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", - "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." + "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", + "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici." } From f348c9daa7b6d0e9d0db2ab9fab8f76506511596 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:23:01 +0000 Subject: [PATCH 041/322] Bump tar-fs from 2.1.3 to 2.1.4 Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.3 to 2.1.4. - [Commits](https://github.com/mafintosh/tar-fs/compare/v2.1.3...v2.1.4) --- updated-dependencies: - dependency-name: tar-fs dependency-version: 2.1.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e060e0a..80585968 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17431,9 +17431,9 @@ } }, "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "license": "MIT", "dependencies": { "chownr": "^1.1.1", From 4eff52ab62229eea5ef8ddfadc2a79819f7a3051 Mon Sep 17 00:00:00 2001 From: "sh.nurmagomedov" Date: Sat, 27 Sep 2025 10:54:14 +0300 Subject: [PATCH 042/322] Add minor version tags to Docker build commands in Makefile --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index de67a5f2..d93cc26d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ .PHONY: build build-pg build-release build-arm build-x86 test clean +minor_tag := $(shell echo $(tag) | cut -d. -f1,2) build-release: @if [ -z "$(tag)" ]; then \ echo "Error: tag is required. Usage: make build-release tag="; \ @@ -7,8 +8,10 @@ build-release: fi docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest --push . docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) --push . + docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(minor_tag) --push . docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-latest --push . docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(tag) --push . + docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(minor_tag) --push . build-arm: docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest . From ca6ae53fe65fb0ec0f9a3824050a1235a4583d26 Mon Sep 17 00:00:00 2001 From: Vitor Ventura Date: Sat, 27 Sep 2025 23:26:47 +0100 Subject: [PATCH 043/322] fix portuguese typo --- messages/pt-PT.json | 272 ++++++++++++++++++++++---------------------- 1 file changed, 136 insertions(+), 136 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 914a7dd3..04baacd9 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -8,25 +8,25 @@ "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", - "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", - "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", + "componentsErrorNoMemberCreate": "Não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Não é atualmente um membro de nenhuma organização.", "welcome": "Bem-vindo ao Pangolin", "welcomeTo": "Bem-vindo ao", "componentsCreateOrg": "Criar uma organização", - "componentsMember": "Você é membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", + "componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", - "dismiss": "Descartar", + "dismiss": "Rejeitar", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", - "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", - "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", - "inviteLoginUser": "Verifique se você está logado como o usuário correto.", - "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que está a tentar aceder não é para este utilizador.", + "inviteLoginUser": "Verifique se você está logado como o utilizador correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que está a tentar aceder não é para um utilizador que existe.", "inviteCreateUser": "Por favor, crie uma conta primeiro.", - "goHome": "Ir para casa", - "inviteLogInOtherUser": "Fazer login como um usuário diferente", + "goHome": "Voltar ao inicio", + "inviteLogInOtherUser": "Fazer login como um utilizador diferente", "createAnAccount": "Crie uma conta", - "inviteNotAccepted": "Convite não aceito", + "inviteNotAccepted": "Convite não aceite", "authCreateAccount": "Crie uma conta para começar", "authNoAccount": "Não possui uma conta?", "email": "e-mail", @@ -34,23 +34,23 @@ "confirmPassword": "Confirmar senha", "createAccount": "Criar conta", "viewSettings": "Visualizar configurações", - "delete": "excluir", + "delete": "apagar", "name": "Nome:", "online": "Disponível", "offline": "Desconectado", "site": "site", - "dataIn": "Dados em", + "dataIn": "Dados de entrada", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", - "siteConfirmDelete": "Confirmar exclusão do site", + "siteConfirmDelete": "Confirmar que pretende apagar o site", "siteDelete": "Excluir site", "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", - "siteManageSites": "Gerenciar sites", + "siteManageSites": "Gerir sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", @@ -79,10 +79,10 @@ "operatingSystem": "Sistema operacional", "commands": "Comandos", "recommended": "Recomendados", - "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteNewtDescription": "Para a melhor experiência do utilizador, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", - "siteErrorDelete": "Erro ao excluir site", + "siteErrorDelete": "Erro ao apagar site", "siteErrorUpdate": "Falha ao atualizar site", "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", "siteUpdated": "Site atualizado", @@ -105,12 +105,12 @@ "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", "siteInfo": "Informações do Site", "status": "SItuação", - "shareTitle": "Gerenciar links de compartilhamento", + "shareTitle": "Gerir links partilhados", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", "shareCreate": "Criar Link de Compartilhamento", - "shareErrorDelete": "Falha ao excluir o link", - "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", + "shareErrorDelete": "Falha ao apagar o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao apagar o link", "shareDeleted": "Link excluído", "shareDeletedDescription": "O link foi eliminado", "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", @@ -127,13 +127,13 @@ "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", "shareErrorCreate": "Falha ao criar link de compartilhamento", "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", - "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", + "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", "shareTitleOptional": "Título (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", - "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", - "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", + "shareAccessHint": "Qualquer um com este link pode aceder o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", "resourcesNotFound": "Nenhum recurso encontrado", @@ -145,11 +145,11 @@ "expires": "Expira", "never": "nunca", "shareErrorSelectResource": "Por favor, selecione um recurso", - "resourceTitle": "Gerenciar Recursos", + "resourceTitle": "Gerir Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", - "resourceErrorDelte": "Erro ao excluir recurso", + "resourceErrorDelte": "Erro ao apagar recurso", "authentication": "Autenticação", "protected": "Protegido", "notProtected": "Não Protegido", @@ -170,7 +170,7 @@ "siteNotFound": "Nenhum site encontrado.", "siteSelectionDescription": "Este site fornecerá conectividade ao destino.", "resourceType": "Tipo de Recurso", - "resourceTypeDescription": "Determine como você deseja acessar seu recurso", + "resourceTypeDescription": "Determine como você deseja aceder seu recurso", "resourceHTTPSSettings": "Configurações de HTTPS", "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", "domainType": "Tipo de domínio", @@ -192,7 +192,7 @@ "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", "resourceDelete": "Excluir Recurso", - "resourceDeleteConfirm": "Confirmar exclusão de recurso", + "resourceDeleteConfirm": "Confirmar que pretende apagar o recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -208,14 +208,14 @@ "passToAuth": "Passar para Autenticação", "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", - "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Salvar configurações gerais", - "saveSettings": "Salvar Configurações", + "orgGeneralSettingsDescription": "Gerir os detalhes e a configuração da sua organização", + "saveGeneralSettings": "Guardar configurações gerais", + "saveSettings": "Guardar Configurações", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", - "orgDeleteConfirm": "Confirmar exclusão da organização", - "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", + "orgDeleteConfirm": "Confirmar que pretende apagar a organização", + "orgMessageRemove": "Esta ação é irreversível e apagará todos os dados associados.", "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", "orgUpdated": "Organização atualizada", @@ -224,29 +224,29 @@ "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", "orgErrorFetch": "Falha ao buscar organizações", "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", - "orgErrorDelete": "Falha ao excluir organização", - "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", + "orgErrorDelete": "Falha ao apagar organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgMissing": "ID da Organização Ausente", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", - "accessUsersManage": "Gerenciar Usuários", - "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", - "accessUsersSearch": "Procurar usuários...", + "accessUsersManage": "Gerir Utilizadores", + "accessUsersDescription": "Convidar utilizadores e adicioná-los a funções para gerir o acesso à sua organização", + "accessUsersSearch": "Procurar utilizadores...", "accessUserCreate": "Criar Usuário", - "accessUserRemove": "Remover usuário", + "accessUserRemove": "Remover utilizador", "username": "Usuário:", "identityProvider": "Provedor de Identidade", "role": "Funções", "nameRequired": "O nome é obrigatório", - "accessRolesManage": "Gerenciar Funções", - "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", + "accessRolesManage": "Gerir Funções", + "accessRolesDescription": "Configurar funções para gerir o acesso à sua organização", "accessRolesSearch": "Pesquisar funções...", "accessRolesAdd": "Adicionar função", "accessRoleDelete": "Excluir Papel", "description": "Descrição:", "inviteTitle": "Convites Abertos", - "inviteDescription": "Gerencie seus convites para outros usuários", + "inviteDescription": "Gerir seus convites para outros utilizadores", "inviteSearch": "Procurar convites...", "minutes": "minutos", "hours": "horas", @@ -264,7 +264,7 @@ "apiKeysGeneralSettings": "Permissões", "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", "apiKeysList": "Sua Chave API", - "apiKeysSave": "Salvar Sua Chave API", + "apiKeysSave": "Guardar Sua Chave API", "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", "apiKeysInfo": "Sua chave API é:", "apiKeysConfirmCopy": "Eu copiei a chave API", @@ -277,33 +277,33 @@ "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", "apiKeysPermissionsGeneralSettings": "Permissões", "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", - "apiKeysPermissionsSave": "Salvar Permissões", + "apiKeysPermissionsSave": "Guardar Permissões", "apiKeysPermissionsTitle": "Permissões", "apiKeys": "Chaves API", "searchApiKeys": "Pesquisar chaves API...", "apiKeysAdd": "Gerar Chave API", - "apiKeysErrorDelete": "Erro ao excluir chave API", - "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", + "apiKeysErrorDelete": "Erro ao apagar chave API", + "apiKeysErrorDeleteMessage": "Erro ao apagar chave API", "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", - "apiKeysManage": "Gerenciar Chaves API", + "apiKeysManage": "Gerir Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", "apiKeysSettings": "Configurações de {apiKeyName}", - "userTitle": "Gerenciar Todos os Usuários", - "userDescription": "Visualizar e gerenciar todos os usuários no sistema", + "userTitle": "Gerir Todos os Utilizadores", + "userDescription": "Visualizar e gerir todos os utilizadores no sistema", "userAbount": "Sobre a Gestão de Usuário", - "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", - "userServer": "Usuários do Servidor", - "userSearch": "Pesquisar usuários do servidor...", - "userErrorDelete": "Erro ao excluir usuário", + "userAbountDescription": "Esta tabela exibe todos os objetos root do utilizador. Cada utilizador pode pertencer a várias organizações. Remover um utilizador de uma organização não exclui seu objeto de utilizador raiz - ele permanecerá no sistema. Para remover completamente um utilizador do sistema, você deve apagar seu objeto raiz usando a ação de apagar nesta tabela.", + "userServer": "Utilizadores do Servidor", + "userSearch": "Pesquisar utilizadores do servidor...", + "userErrorDelete": "Erro ao apagar utilizador", "userDeleteConfirm": "Confirmar Exclusão do Usuário", - "userDeleteServer": "Excluir usuário do servidor", - "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", - "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", - "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", + "userDeleteServer": "Excluir utilizador do servidor", + "userMessageRemove": "O utilizador será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do utilizador abaixo.", + "userQuestionRemove": "Tem certeza que deseja apagar o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", "valid": "Válido", "numberOfSites": "Número de sites", @@ -314,8 +314,8 @@ "licenseTermsAgree": "Você deve concordar com os termos da licença", "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", - "licenseErrorKeyDelete": "Falha ao excluir chave de licença", - "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", + "licenseErrorKeyDelete": "Falha ao apagar chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao apagar a chave de licença.", "licenseKeyDeleted": "Chave da licença excluída", "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", @@ -336,13 +336,13 @@ "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", - "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", + "licenseQuestionRemove": "Tem certeza que deseja apagar a chave de licença {selectedKey}?", "licenseKeyDelete": "Excluir Chave de Licença", - "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", - "licenseTitle": "Gerenciar Status da Licença", - "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", + "licenseKeyDeleteConfirm": "Confirmar que pretende apagar a chave de licença", + "licenseTitle": "Gerir Status da Licença", + "licenseTitleDescription": "Visualizar e gerir chaves de licença no sistema", "licenseHost": "Licença do host", - "licenseHostDescription": "Gerenciar a chave de licença principal do host.", + "licenseHostDescription": "Gerir a chave de licença principal do host.", "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", @@ -370,37 +370,37 @@ "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", - "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o utilizador novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", "inviteRemoveConfirm": "Confirmar Remoção do Convite", "inviteRegenerated": "Convite Regenerado", "inviteSent": "Um novo convite foi enviado para {email}.", - "inviteSentEmail": "Enviar notificação por e-mail ao usuário", + "inviteSentEmail": "Enviar notificação por e-mail ao utilizador", "inviteGenerate": "Um novo convite foi gerado para {email}.", "inviteDuplicateError": "Convite Duplicado", - "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", + "inviteDuplicateErrorDescription": "Já existe um convite para este utilizador.", "inviteRateLimitError": "Limite de Taxa Excedido", - "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRateLimitErrorDescription": "Excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", "inviteRegenerateError": "Falha ao Regenerar Convite", "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", "inviteValidityPeriod": "Período de Validade", "inviteValidityPeriodSelect": "Selecione o período de validade", - "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", + "inviteRegenerateMessage": "O convite foi regenerado. O utilizador deve aceder o link abaixo para aceitar o convite.", "inviteRegenerateButton": "Regenerar", "expiresAt": "Expira em", "accessRoleUnknown": "Função Desconhecida", "placeholder": "Espaço reservado", - "userErrorOrgRemove": "Falha ao remover usuário", - "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", + "userErrorOrgRemove": "Falha ao remover utilizador", + "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o utilizador.", "userOrgRemoved": "Usuário removido", - "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", + "userOrgRemovedDescription": "O utilizador {email} foi removido da organização.", "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", - "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", - "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", + "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", + "userMessageOrgConfirm": "Para confirmar, digite o nome do utilizador abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrg": "Remover Usuário da Organização", - "users": "Usuários", + "users": "Utilizadores", "accessRoleMember": "Membro", "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", @@ -408,7 +408,7 @@ "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", - "usernameRequired": "Nome de usuário é obrigatório", + "usernameRequired": "Nome de utilizador é obrigatório", "idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "accessRoleErrorFetch": "Falha ao buscar funções", @@ -416,51 +416,51 @@ "idpErrorFetch": "Falha ao buscar provedores de identidade", "idpErrorFetchDescription": "Ocorreu um erro ao buscar provedores de identidade", "userErrorExists": "Usuário já existe", - "userErrorExistsDescription": "Este usuário já é membro da organização.", - "inviteError": "Falha ao convidar usuário", - "inviteErrorDescription": "Ocorreu um erro ao convidar o usuário", + "userErrorExistsDescription": "Este utilizador já é membro da organização.", + "inviteError": "Falha ao convidar utilizador", + "inviteErrorDescription": "Ocorreu um erro ao convidar o utilizador", "userInvited": "Usuário convidado", - "userInvitedDescription": "O usuário foi convidado com sucesso.", - "userErrorCreate": "Falha ao criar usuário", - "userErrorCreateDescription": "Ocorreu um erro ao criar o usuário", + "userInvitedDescription": "O utilizador foi convidado com sucesso.", + "userErrorCreate": "Falha ao criar utilizador", + "userErrorCreateDescription": "Ocorreu um erro ao criar o utilizador", "userCreated": "Usuário criado", - "userCreatedDescription": "O usuário foi criado com sucesso.", + "userCreatedDescription": "O utilizador foi criado com sucesso.", "userTypeInternal": "Usuário Interno", - "userTypeInternalDescription": "Convidar um usuário para se juntar à sua organização diretamente.", + "userTypeInternalDescription": "Convidar um utilizador para se juntar à sua organização diretamente.", "userTypeExternal": "Usuário Externo", - "userTypeExternalDescription": "Criar um usuário com um provedor de identidade externo.", - "accessUserCreateDescription": "Siga os passos abaixo para criar um novo usuário", - "userSeeAll": "Ver Todos os Usuários", + "userTypeExternalDescription": "Criar um utilizador com um provedor de identidade externo.", + "accessUserCreateDescription": "Siga os passos abaixo para criar um novo utilizador", + "userSeeAll": "Ver Todos os Utilizadores", "userTypeTitle": "Tipo de Usuário", - "userTypeDescription": "Determine como você deseja criar o usuário", + "userTypeDescription": "Determine como você deseja criar o utilizador", "userSettings": "Informações do Usuário", - "userSettingsDescription": "Insira os detalhes para o novo usuário", - "inviteEmailSent": "Enviar e-mail de convite para o usuário", + "userSettingsDescription": "Insira os detalhes para o novo utilizador", + "inviteEmailSent": "Enviar e-mail de convite para o utilizador", "inviteValid": "Válido Por", "selectDuration": "Selecionar duração", "accessRoleSelect": "Selecionar função", - "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", - "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", + "inviteEmailSentDescription": "Um e-mail foi enviado ao utilizador com o link de acesso abaixo. Eles devem aceder ao link para aceitar o convite.", + "inviteSentDescription": "O utilizador foi convidado. Eles devem aceder ao link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, one {# dia} other {# dias}}.", "idpTitle": "Informações Gerais", - "idpSelect": "Selecione o provedor de identidade para o usuário externo", - "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", - "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", + "idpSelect": "Selecione o provedor de identidade para o utilizador externo", + "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar utilizadores externos.", + "usernameUniq": "Isto deve corresponder ao nome de utilizador único que existe no provedor de identidade selecionado.", "emailOptional": "E-mail (Opcional)", "nameOptional": "Nome (Opcional)", - "accessControls": "Controles de Acesso", - "userDescription2": "Gerenciar as configurações deste usuário", - "accessRoleErrorAdd": "Falha ao adicionar usuário à função", - "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar usuário à função.", + "accessControls": "Controlos de Acesso", + "userDescription2": "Gerir as configurações deste utilizador", + "accessRoleErrorAdd": "Falha ao adicionar utilizador à função", + "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar utilizador à função.", "userSaved": "Usuário salvo", - "userSavedDescription": "O usuário foi atualizado.", + "userSavedDescription": "O utilizador foi atualizado.", "autoProvisioned": "Auto provisionado", - "autoProvisionedDescription": "Permitir que este usuário seja gerenciado automaticamente pelo provedor de identidade", - "accessControlsDescription": "Gerencie o que este usuário pode acessar e fazer na organização", - "accessControlsSubmit": "Salvar Controles de Acesso", + "autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade", + "accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização", + "accessControlsSubmit": "Guardar Controlos de Acesso", "roles": "Funções", - "accessUsersRoles": "Gerenciar Usuários e Funções", - "accessUsersRolesDescription": "Convide usuários e adicione-os a funções para gerenciar o acesso à sua organização", + "accessUsersRoles": "Gerir Utilizadores e Funções", + "accessUsersRolesDescription": "Convide utilizadores e adicione-os a funções para gerir o acesso à sua organização", "key": "Chave", "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", @@ -494,7 +494,7 @@ "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", - "targetTlsSubmit": "Salvar Configurações", + "targetTlsSubmit": "Guardar Configurações", "targets": "Configuração de Alvos", "targetsDescription": "Configure alvos para rotear tráfego para seus serviços de backend", "targetStickySessions": "Ativar Sessões Persistentes", @@ -503,12 +503,12 @@ "targetSubmit": "Adicionar Alvo", "targetNoOne": "Sem alvos. Adicione um alvo usando o formulário.", "targetNoOneDescription": "Adicionar mais de um alvo acima habilitará o balanceamento de carga.", - "targetsSubmit": "Salvar Alvos", + "targetsSubmit": "Guardar Alvos", "proxyAdditional": "Configurações Adicionais de Proxy", "proxyAdditionalDescription": "Configure como seu recurso lida com configurações de proxy", "proxyCustomHeader": "Cabeçalho Host Personalizado", "proxyCustomHeaderDescription": "O cabeçalho host para definir ao fazer proxy de requisições. Deixe vazio para usar o padrão.", - "proxyAdditionalSubmit": "Salvar Configurações de Proxy", + "proxyAdditionalSubmit": "Guardar Configurações de Proxy", "subnetMaskErrorInvalid": "Máscara de subnet inválida. Deve estar entre 0 e 32.", "ipAddressErrorInvalidFormat": "Formato de endereço IP inválido", "ipAddressErrorInvalidOctet": "Octeto de endereço IP inválido", @@ -561,7 +561,7 @@ "ruleSubmit": "Adicionar Regra", "rulesNoOne": "Sem regras. Adicione uma regra usando o formulário.", "rulesOrder": "As regras são avaliadas por prioridade em ordem ascendente.", - "rulesSubmit": "Salvar Regras", + "rulesSubmit": "Guardar Regras", "resourceErrorCreate": "Erro ao criar recurso", "resourceErrorCreateDescription": "Ocorreu um erro ao criar o recurso", "resourceErrorCreateMessage": "Erro ao criar recurso:", @@ -576,7 +576,7 @@ "resourcesDescription": "Recursos são proxies para aplicações executando em sua rede privada. Crie um recurso para qualquer serviço HTTP/HTTPS ou TCP/UDP bruto em sua rede privada. Cada recurso deve estar conectado a um site para habilitar conectividade privada e segura através de um túnel WireGuard criptografado.", "resourcesWireGuardConnect": "Conectividade segura com criptografia WireGuard", "resourcesMultipleAuthenticationMethods": "Configure múltiplos métodos de autenticação", - "resourcesUsersRolesAccess": "Controle de acesso baseado em usuários e funções", + "resourcesUsersRolesAccess": "Controle de acesso baseado em utilizadores e funções", "resourcesErrorUpdate": "Falha ao alternar recurso", "resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso", "access": "Acesso", @@ -606,7 +606,7 @@ "pangolinSettings": "Configurações - Pangolin", "accessRoleYour": "Sua função:", "accessRoleSelect2": "Selecionar uma função", - "accessUserSelect": "Selecionar um usuário", + "accessUserSelect": "Selecionar um utilizador", "otpEmailEnter": "Digite um e-mail", "otpEmailEnterDescription": "Pressione enter para adicionar um e-mail após digitá-lo no campo de entrada.", "otpEmailErrorInvalid": "Endereço de e-mail inválido. O caractere curinga (*) deve ser a parte local inteira.", @@ -616,8 +616,8 @@ "otpEmailTitleDescription": "Requer autenticação baseada em e-mail para acesso ao recurso", "otpEmailWhitelist": "Lista de E-mails Permitidos", "otpEmailWhitelistList": "E-mails na Lista Permitida", - "otpEmailWhitelistListDescription": "Apenas usuários com estes endereços de e-mail poderão acessar este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", - "otpEmailWhitelistSave": "Salvar Lista Permitida", + "otpEmailWhitelistListDescription": "Apenas utilizadores com estes endereços de e-mail poderão aceder este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", + "otpEmailWhitelistSave": "Guardar Lista Permitida", "passwordAdd": "Adicionar Senha", "passwordRemove": "Remover Senha", "pincodeAdd": "Adicionar Código PIN", @@ -657,14 +657,14 @@ "resourcePincodeSetupDescription": "O código PIN do recurso foi definido com sucesso", "resourcePincodeSetupTitle": "Definir Código PIN", "resourcePincodeSetupTitleDescription": "Defina um código PIN para proteger este recurso", - "resourceRoleDescription": "Administradores sempre podem acessar este recurso.", - "resourceUsersRoles": "Usuários e Funções", - "resourceUsersRolesDescription": "Configure quais usuários e funções podem visitar este recurso", - "resourceUsersRolesSubmit": "Salvar Usuários e Funções", + "resourceRoleDescription": "Administradores sempre podem aceder este recurso.", + "resourceUsersRoles": "Utilizadores e Funções", + "resourceUsersRolesDescription": "Configure quais utilizadores e funções podem visitar este recurso", + "resourceUsersRolesSubmit": "Guardar Utilizadores e Funções", "resourceWhitelistSave": "Salvo com sucesso", "resourceWhitelistSaveDescription": "As configurações da lista permitida foram salvas", "ssoUse": "Usar SSO da Plataforma", - "ssoUseDescription": "Os usuários existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", + "ssoUseDescription": "Os utilizadores existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", "proxyErrorInvalidPort": "Número da porta inválido", "subdomainErrorInvalid": "Subdomínio inválido", "domainErrorFetch": "Erro ao buscar domínios", @@ -690,7 +690,7 @@ "siteDestination": "Site de Destino", "searchSites": "Pesquisar sites", "accessRoleCreate": "Criar Função", - "accessRoleCreateDescription": "Crie uma nova função para agrupar usuários e gerenciar suas permissões.", + "accessRoleCreateDescription": "Crie uma nova função para agrupar utilizadores e gerir suas permissões.", "accessRoleCreateSubmit": "Criar Função", "accessRoleCreated": "Função criada", "accessRoleCreatedDescription": "A função foi criada com sucesso.", @@ -700,13 +700,13 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a apagar a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", "accessRoleRemoved": "Função removida", "accessRoleRemovedDescription": "A função foi removida com sucesso.", - "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", + "accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", @@ -918,8 +918,8 @@ "idpAzureAlt": "Azure", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", - "inviteErrorWrongUser": "O convite não é para este usuário", - "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", + "inviteErrorWrongUser": "O convite não é para este utilizador", + "inviteErrorUserNotExists": "O utilizador não existe. Por favor, crie uma conta primeiro.", "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", "inviteErrorExpired": "O convite pode ter expirado", "inviteErrorRevoked": "O convite pode ter sido revogado", @@ -934,7 +934,7 @@ "home": "Início", "accessControl": "Controle de Acesso", "settings": "Configurações", - "usersAll": "Todos os Usuários", + "usersAll": "Todos os Utilizadores", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", "noResults": "Nenhum resultado encontrado.", @@ -987,8 +987,8 @@ "licenseTierProfessionalRequired": "Edição Profissional Necessária", "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", "actionGetOrg": "Obter Organização", - "updateOrgUser": "Atualizar usuário Org", - "createOrgUser": "Criar usuário Org", + "updateOrgUser": "Atualizar utilizador Org", + "createOrgUser": "Criar utilizador Org", "actionUpdateOrg": "Atualizar Organização", "actionUpdateUser": "Atualizar Usuário", "actionGetUser": "Obter Usuário", @@ -1135,8 +1135,8 @@ "sidebarRoles": "Papéis", "sidebarShareableLinks": "Links compartilháveis", "sidebarApiKeys": "Chaves API", - "sidebarSettings": "Confirgurações", - "sidebarAllUsers": "Todos os usuários", + "sidebarSettings": "Configurações", + "sidebarAllUsers": "Todos os utilizadores", "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "sidebarClients": "Clientes (Beta)", @@ -1189,7 +1189,7 @@ "loading": "Carregando", "restart": "Reiniciar", "domains": "Domínios", - "domainsDescription": "Gerencie domínios para sua organização", + "domainsDescription": "Gerir domínios para sua organização", "domainsSearch": "Pesquisar domínios...", "domainAdd": "Adicionar Domínio", "domainAddDescription": "Registre um novo domínio com sua organização", @@ -1217,7 +1217,7 @@ "pending": "Pendente", "sidebarBilling": "Faturamento", "billing": "Faturamento", - "orgBillingDescription": "Gerencie suas informações de faturamento e assinaturas", + "orgBillingDescription": "Gerir suas informações de faturação e assinaturas", "github": "GitHub", "pangolinHosted": "Hospedagem Pangolin", "fossorial": "Fossorial", @@ -1232,7 +1232,7 @@ "completeSetup": "Configuração Completa", "accountSetupSuccess": "Configuração da conta concluída! Bem-vindo ao Pangolin!", "documentation": "Documentação", - "saveAllSettings": "Salvar Todas as Configurações", + "saveAllSettings": "Guardar Todas as Configurações", "settingsUpdated": "Configurações atualizadas", "settingsUpdatedDescription": "Todas as configurações foram atualizadas com sucesso", "settingsErrorUpdate": "Falha ao atualizar configurações", @@ -1263,7 +1263,7 @@ "createNewOrgDescription": "Crie uma nova organização", "organization": "Organização", "port": "Porta", - "securityKeyManage": "Gerenciar chaves de segurança", + "securityKeyManage": "Gerir chaves de segurança", "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", "securityKeyRegister": "Registrar nova chave de segurança", "securityKeyList": "Suas chaves de segurança", @@ -1314,7 +1314,7 @@ "createDomainARecords": "Registros A", "createDomainRecordNumber": "Registrar {number}", "createDomainTxtRecords": "Registros TXT", - "createDomainSaveTheseRecords": "Salvar Esses Registros", + "createDomainSaveTheseRecords": "Guardar Esses Registros", "createDomainSaveTheseRecordsDescription": "Certifique-se de salvar esses registros DNS, pois você não os verá novamente.", "createDomainDnsPropagation": "Propagação DNS", "createDomainDnsPropagationDescription": "Alterações no DNS podem levar algum tempo para se propagar pela internet. Pode levar de alguns minutos a 48 horas, dependendo do seu provedor de DNS e das configurações de TTL.", @@ -1401,7 +1401,7 @@ "editInternalResourceDialogSitePort": "Porta do Site", "editInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "editInternalResourceDialogCancel": "Cancelar", - "editInternalResourceDialogSaveResource": "Salvar Recurso", + "editInternalResourceDialogSaveResource": "Guardar Recurso", "editInternalResourceDialogSuccess": "Sucesso", "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno atualizado com sucesso", "editInternalResourceDialogError": "Erro", @@ -1428,7 +1428,7 @@ "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", "createInternalResourceDialogSitePort": "Porta do Site", - "createInternalResourceDialogSitePortDescription": "Use esta porta para acessar o recurso no site quando conectado com um cliente.", + "createInternalResourceDialogSitePortDescription": "Use esta porta para aceder o recurso no site quando conectado com um cliente.", "createInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "createInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "createInternalResourceDialogDestinationPortDescription": "A porta no IP de destino onde o recurso está acessível.", @@ -1452,7 +1452,7 @@ "siteAddress": "Endereço do Site", "siteAddressDescription": "Especificar o endereço IP do host para que os clientes se conectem. Este é o endereço interno do site na rede Pangolin para os clientes endereçarem. Deve estar dentro da sub-rede da Organização.", "autoLoginExternalIdp": "Login Automático com IDP Externo", - "autoLoginExternalIdpDescription": "Redirecionar imediatamente o usuário para o IDP externo para autenticação.", + "autoLoginExternalIdpDescription": "Redirecionar imediatamente o utilizador para o IDP externo para autenticação.", "selectIdp": "Selecionar IDP", "selectIdpPlaceholder": "Escolher um IDP...", "selectIdpRequired": "Por favor, selecione um IDP quando o login automático estiver ativado.", @@ -1479,7 +1479,7 @@ }, "benefitLessMaintenance": { "title": "Menos manutenção", - "description": "Sem migrações, backups ou infraestrutura extra para gerenciar. Lidamos com isso na nuvem." + "description": "Sem migrações, backups ou infraestrutura extra para gerir. Lidamos com isso na nuvem." }, "benefitCloudFailover": { "title": "Falha na nuvem", From 1a01e8d53a856fca19632ad27ca67bcb33517070 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 27 Sep 2025 16:59:41 -0700 Subject: [PATCH 044/322] Update readme crowdin --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8e55cf12..458103c4 100644 --- a/README.md +++ b/README.md @@ -153,3 +153,5 @@ Looking for something to contribute? Take a look at issues marked with [help wan Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices. Please post bug reports and other functional issues in the [Issues](https://github.com/fosrl/pangolin/issues) section of the repository. + +If you are looking to help with translations, please contribute [on Crowdin](https://crowdin.com/project/fossorial-pangolin) or open a PR with changes to the translations files found in `messages/`. \ No newline at end of file From a06e8c8f83b4f0f9053dffa022fb3d52827f1432 Mon Sep 17 00:00:00 2001 From: "sh.nurmagomedov" Date: Sun, 28 Sep 2025 12:17:29 +0300 Subject: [PATCH 045/322] Add major version tags to Docker build commands in Makefile --- Makefile | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index d93cc26d..e4a709ec 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,28 @@ .PHONY: build build-pg build-release build-arm build-x86 test clean +major_tag := $(shell echo $(tag) | cut -d. -f1) minor_tag := $(shell echo $(tag) | cut -d. -f1,2) build-release: @if [ -z "$(tag)" ]; then \ echo "Error: tag is required. Usage: make build-release tag="; \ exit 1; \ fi - docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest --push . - docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) --push . - docker buildx build --build-arg DATABASE=sqlite --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(minor_tag) --push . - docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-latest --push . - docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(tag) --push . - docker buildx build --build-arg DATABASE=pg --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(minor_tag) --push . + docker buildx build \ + --build-arg DATABASE=sqlite \ + --platform linux/arm64,linux/amd64 \ + --tag fosrl/pangolin:latest \ + --tag fosrl/pangolin:$(major_tag) \ + --tag fosrl/pangolin:$(minor_tag) \ + --tag fosrl/pangolin:$(tag) \ + --push . + docker buildx build \ + --build-arg DATABASE=pg \ + --platform linux/arm64,linux/amd64 \ + --tag fosrl/pangolin:postgresql-latest \ + --tag fosrl/pangolin:postgresql-$(major_tag) \ + --tag fosrl/pangolin:postgresql-$(minor_tag) \ + --tag fosrl/pangolin:postgresql-$(tag) \ + --push . build-arm: docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest . From 2c8082451f947a741a1608602c37aadff9895811 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 10:32:46 -0700 Subject: [PATCH 046/322] Add where clause to sql migrations --- server/setup/scriptsPg/1.10.4.ts | 2 ++ server/setup/scriptsSqlite/1.10.4.ts | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts index 311e6dc2..fa4ff401 100644 --- a/server/setup/scriptsPg/1.10.4.ts +++ b/server/setup/scriptsPg/1.10.4.ts @@ -18,11 +18,13 @@ export default async function migration() { const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); await db.execute(sql` UPDATE "webauthnCredentials" SET "credentialId" = ${credentialId} + WHERE "credentialId" = ${webauthnCredential.credentialId} `); const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); await db.execute(sql` UPDATE "webauthnCredentials" SET "publicKey" = ${publicKey} + WHERE "credentialId" = ${webauthnCredential.credentialId} `); } diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts index 5c7f0a0e..ff22d70f 100644 --- a/server/setup/scriptsSqlite/1.10.4.ts +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -20,13 +20,13 @@ export default async function migration() { for (const webauthnCredential of webauthnCredentials) { const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); db.prepare( - `UPDATE 'webauthnCredentials' SET 'credentialId' = ?` - ).run(credentialId); + `UPDATE 'webauthnCredentials' SET 'credentialId' = ? WHERE 'credentialId' = ?` + ).run(credentialId, webauthnCredential.credentialId); const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); db.prepare( - `UPDATE 'webauthnCredentials' SET 'publicKey' = ?` - ).run(publicKey); + `UPDATE 'webauthnCredentials' SET 'publicKey' = ? WHERE 'credentialId' = ?` + ).run(publicKey, webauthnCredential.credentialId); } })(); From 4523a8df0fe299ae7948951497cdda5d5d0b1a95 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 10:36:03 -0700 Subject: [PATCH 047/322] Bump build --- server/lib/consts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 6c13963a..5df517b8 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.10.2"; +export const APP_VERSION = "1.10.4"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); From e43fc59634449a65c73f624562a5c71690273087 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 10:39:09 -0700 Subject: [PATCH 048/322] Use double quotes --- server/setup/scriptsPg/1.10.4.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts index fa4ff401..dafec24b 100644 --- a/server/setup/scriptsPg/1.10.4.ts +++ b/server/setup/scriptsPg/1.10.4.ts @@ -10,7 +10,7 @@ export default async function migration() { try { await db.execute(sql`BEGIN`); - const webauthnCredentialsQuery = await db.execute(sql`SELECT credentialId, publicKey FROM 'webauthnCredentials'`); + const webauthnCredentialsQuery = await db.execute(sql`SELECT "credentialId", "publicKey" FROM "webauthnCredentials"`); const webauthnCredentials = webauthnCredentialsQuery.rows as { credentialId: string; publicKey: string }[]; From 88d97dd49b18bd80ec2dc6c4aa106aba66f730d2 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 11:12:41 -0700 Subject: [PATCH 049/322] Fix migration --- server/setup/scriptsPg/1.10.4.ts | 28 ++++++++++++++++------- server/setup/scriptsSqlite/1.10.4.ts | 34 ++++++++++++++++++---------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts index dafec24b..77df637c 100644 --- a/server/setup/scriptsPg/1.10.4.ts +++ b/server/setup/scriptsPg/1.10.4.ts @@ -10,21 +10,33 @@ export default async function migration() { try { await db.execute(sql`BEGIN`); - const webauthnCredentialsQuery = await db.execute(sql`SELECT "credentialId", "publicKey" FROM "webauthnCredentials"`); + const webauthnCredentialsQuery = await db.execute(sql`SELECT "credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated" FROM "webauthnCredentials"`); - const webauthnCredentials = webauthnCredentialsQuery.rows as { credentialId: string; publicKey: string }[]; + const webauthnCredentials = webauthnCredentialsQuery.rows as { + credentialId: string; + publicKey: string; + userId: string; + signCount: number; + transports: string | null; + name: string | null; + lastUsed: string; + dateCreated: string; + }[]; for (const webauthnCredential of webauthnCredentials) { - const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); + const newCredentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); + const newPublicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); + + // Delete the old record await db.execute(sql` - UPDATE "webauthnCredentials" SET "credentialId" = ${credentialId} + DELETE FROM "webauthnCredentials" WHERE "credentialId" = ${webauthnCredential.credentialId} `); - - const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); + + // Insert the updated record with converted values await db.execute(sql` - UPDATE "webauthnCredentials" SET "publicKey" = ${publicKey} - WHERE "credentialId" = ${webauthnCredential.credentialId} + INSERT INTO "webauthnCredentials" ("credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated") + VALUES (${newCredentialId}, ${newPublicKey}, ${webauthnCredential.userId}, ${webauthnCredential.signCount}, ${webauthnCredential.transports}, ${webauthnCredential.name}, ${webauthnCredential.lastUsed}, ${webauthnCredential.dateCreated}) `); } diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts index ff22d70f..cd00a65e 100644 --- a/server/setup/scriptsSqlite/1.10.4.ts +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -13,21 +13,31 @@ export default async function migration() { db.transaction(() => { - const webauthnCredentials = db.prepare(`SELECT credentialId, publicKey FROM 'webauthnCredentials'`).all() as { - credentialId: string; publicKey: string + const webauthnCredentials = db.prepare(`SELECT credentialId, publicKey, userId, signCount, transports, name, lastUsed, dateCreated FROM 'webauthnCredentials'`).all() as { + credentialId: string; publicKey: string; userId: string; signCount: number; transports: string | null; name: string | null; lastUsed: string; dateCreated: string; }[]; for (const webauthnCredential of webauthnCredentials) { - const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); - db.prepare( - `UPDATE 'webauthnCredentials' SET 'credentialId' = ? WHERE 'credentialId' = ?` - ).run(credentialId, webauthnCredential.credentialId); - - const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); - db.prepare( - `UPDATE 'webauthnCredentials' SET 'publicKey' = ? WHERE 'credentialId' = ?` - ).run(publicKey, webauthnCredential.credentialId); - } + const newCredentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); + const newPublicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); + + // Delete the old record + db.prepare(`DELETE FROM 'webauthnCredentials' WHERE 'credentialId' = ?`).run(webauthnCredential.credentialId); + + // Insert the updated record with converted values + db.prepare( + `INSERT INTO 'webauthnCredentials' (credentialId, publicKey, userId, signCount, transports, name, lastUsed, dateCreated) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` + ).run( + newCredentialId, + newPublicKey, + webauthnCredential.userId, + webauthnCredential.signCount, + webauthnCredential.transports, + webauthnCredential.name, + webauthnCredential.lastUsed, + webauthnCredential.dateCreated + ); + } })(); console.log(`${version} migration complete`); From 516b3007318bfa94cecc098907cd6615cf266dac Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 11:39:18 -0700 Subject: [PATCH 050/322] Use olm install script --- .../[orgId]/settings/clients/create/page.tsx | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/app/[orgId]/settings/clients/create/page.tsx b/src/app/[orgId]/settings/clients/create/page.tsx index 8155a2d6..e5765aea 100644 --- a/src/app/[orgId]/settings/clients/create/page.tsx +++ b/src/app/[orgId]/settings/clients/create/page.tsx @@ -150,40 +150,41 @@ export default function Page() { const commands = { mac: { "Apple Silicon (arm64)": [ - `curl -L -o olm "https://github.com/fosrl/olm/releases/download/${version}/olm_darwin_arm64" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ], "Intel x64 (amd64)": [ - `curl -L -o olm "https://github.com/fosrl/olm/releases/download/${version}/olm_darwin_amd64" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ] }, linux: { amd64: [ - `wget -O olm "https://github.com/fosrl/olm/releases/download/${version}/olm_linux_amd64" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ], arm64: [ - `wget -O olm "https://github.com/fosrl/olm/releases/download/${version}/olm_linux_arm64" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ], arm32: [ - `wget -O olm "https://github.com/fosrl/olm/releases/download/${version}/olm_linux_arm32" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ], arm32v6: [ - `wget -O olm "https://github.com/fosrl/olm/releases/download/${version}/olm_linux_arm32v6" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ], riscv64: [ - `wget -O olm "https://github.com/fosrl/olm/releases/download/${version}/olm_linux_riscv64" && chmod +x ./olm`, - `sudo ./olm --id ${id} --secret ${secret} --endpoint ${endpoint}` + `curl -fsSL https://digpangolin.com/get-olm.sh | bash`, + `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}` ] }, windows: { x64: [ + `# Download and run the installer`, `curl -o olm.exe -L "https://github.com/fosrl/olm/releases/download/${version}/olm_windows_installer.exe"`, - `# Run the installer to install olm and wintun`, + `# Then run olm with your credentials`, `olm.exe --id ${id} --secret ${secret} --endpoint ${endpoint}` ] } From 3872831bd799a681a6058af981c4356d1fd756e9 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 12:21:15 -0700 Subject: [PATCH 051/322] clean up sidebar --- .../[orgId]/settings/sites/create/page.tsx | 8 +-- src/app/admin/managed/page.tsx | 4 +- src/components/LayoutSidebar.tsx | 55 +------------------ 3 files changed, 8 insertions(+), 59 deletions(-) diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index ad5438f7..c2990c78 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -42,7 +42,7 @@ import { FaFreebsd, FaWindows } from "react-icons/fa"; -import { +import { SiNixos, SiKubernetes } from "react-icons/si"; @@ -513,14 +513,14 @@ WantedBy=default.target` try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 3000); - + const response = await fetch( `https://api.github.com/repos/fosrl/newt/releases/latest`, { signal: controller.signal } ); - + clearTimeout(timeoutId); - + if (!response.ok) { throw new Error( t("newtErrorFetchReleases", { diff --git a/src/app/admin/managed/page.tsx b/src/app/admin/managed/page.tsx index 63244d3b..f7a8f70b 100644 --- a/src/app/admin/managed/page.tsx +++ b/src/app/admin/managed/page.tsx @@ -35,11 +35,11 @@ export default function ManagedPage() { -

+

{t("managedSelfHosted.introTitle")}{" "} {t("managedSelfHosted.introDescription")}

-

+

{t("managedSelfHosted.introDetail")}

diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx index b563d9ac..5da9adb2 100644 --- a/src/components/LayoutSidebar.tsx +++ b/src/components/LayoutSidebar.tsx @@ -75,35 +75,6 @@ export function LayoutSidebar({
{!isAdminPage && user.serverAdmin && (
- {build === "oss" && ( - - - - - {!isSidebarCollapsed && ( - {t("managedSelfhosted")} - )} - - )} -
-
+
{!isSidebarCollapsed && (
@@ -155,28 +126,6 @@ export function LayoutSidebar({
-
- - {t("documentation")} - - -
-
- - Discord - - -
{env?.app?.version && (
setIsSidebarCollapsed(!isSidebarCollapsed) } - className="cursor-pointer absolute -right-2.5 top-1/2 transform -translate-y-1/2 w-2 h-8 rounded-full flex items-center justify-center transition-all duration-200 ease-in-out hover:scale-110 group z-[60]" + className="cursor-pointer absolute -right-2.5 top-1/2 transform -translate-y-1/2 w-2 h-8 rounded-full flex items-center justify-center transition-all duration-200 ease-in-out hover:scale-110 group z-1" aria-label={ isSidebarCollapsed ? "Expand sidebar" From 1a13694843466db77de35985428e43b9cd37cb7b Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 12:23:30 -0700 Subject: [PATCH 052/322] gray out time selector in share links if never expire is checked --- src/components/CreateShareLinkForm.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/CreateShareLinkForm.tsx b/src/components/CreateShareLinkForm.tsx index e3ba3f17..59d4d8c6 100644 --- a/src/components/CreateShareLinkForm.tsx +++ b/src/components/CreateShareLinkForm.tsx @@ -388,6 +388,7 @@ export default function CreateShareLinkForm({ field.onChange } defaultValue={field.value.toString()} + disabled={neverExpire} > @@ -423,6 +424,7 @@ export default function CreateShareLinkForm({ ( From 8851156f2332d7fac2f04db3722834a18d7488d9 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 16:22:26 -0700 Subject: [PATCH 053/322] use resource guid in url closes #1517 --- server/db/pg/schema.ts | 14 +++- server/db/sqlite/schema.ts | 9 ++- server/routers/badger/verifySession.ts | 2 +- server/routers/external.ts | 2 +- .../routers/resource/getResourceAuthInfo.ts | 13 ++-- server/setup/scriptsPg/1.10.4.ts | 65 ++++++++++++---- server/setup/scriptsPg/1.8.0.ts | 2 +- server/setup/scriptsSqlite/1.10.4.ts | 78 +++++++++++++++---- .../{[resourceId] => [resourceGuid]}/page.tsx | 12 ++- 9 files changed, 144 insertions(+), 53 deletions(-) rename src/app/auth/resource/{[resourceId] => [resourceGuid]}/page.tsx (95%) diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 3cb5486b..5e34b033 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -9,6 +9,7 @@ import { text } from "drizzle-orm/pg-core"; import { InferSelectModel } from "drizzle-orm"; +import { randomUUID } from "crypto"; export const domains = pgTable("domains", { domainId: varchar("domainId").primaryKey(), @@ -66,6 +67,10 @@ export const sites = pgTable("sites", { export const resources = pgTable("resources", { resourceId: serial("resourceId").primaryKey(), + resourceGuid: varchar("resourceGuid", { length: 36 }) + .unique() + .notNull() + .$defaultFn(() => randomUUID()), orgId: varchar("orgId") .references(() => orgs.orgId, { onDelete: "cascade" @@ -96,7 +101,7 @@ export const resources = pgTable("resources", { skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, { onDelete: "cascade" }), - headers: text("headers"), // comma-separated list of headers to add to the request + headers: text("headers") // comma-separated list of headers to add to the request }); export const targets = pgTable("targets", { @@ -117,7 +122,7 @@ export const targets = pgTable("targets", { internalPort: integer("internalPort"), enabled: boolean("enabled").notNull().default(true), path: text("path"), - pathMatchType: text("pathMatchType"), // exact, prefix, regex + pathMatchType: text("pathMatchType") // exact, prefix, regex }); export const exitNodes = pgTable("exitNodes", { @@ -135,7 +140,8 @@ export const exitNodes = pgTable("exitNodes", { region: varchar("region") }); -export const siteResources = pgTable("siteResources", { // this is for the clients +export const siteResources = pgTable("siteResources", { + // this is for the clients siteResourceId: serial("siteResourceId").primaryKey(), siteId: integer("siteId") .notNull() @@ -149,7 +155,7 @@ export const siteResources = pgTable("siteResources", { // this is for the clien proxyPort: integer("proxyPort").notNull(), destinationPort: integer("destinationPort").notNull(), destinationIp: varchar("destinationIp").notNull(), - enabled: boolean("enabled").notNull().default(true), + enabled: boolean("enabled").notNull().default(true) }); export const users = pgTable("user", { diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 7362f28a..58827b00 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -1,3 +1,4 @@ +import { randomUUID } from "crypto"; import { InferSelectModel } from "drizzle-orm"; import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; @@ -72,6 +73,10 @@ export const sites = sqliteTable("sites", { export const resources = sqliteTable("resources", { resourceId: integer("resourceId").primaryKey({ autoIncrement: true }), + resourceGuid: text("resourceGuid", { length: 36 }) + .unique() + .notNull() + .$defaultFn(() => randomUUID()), orgId: text("orgId") .references(() => orgs.orgId, { onDelete: "cascade" @@ -108,7 +113,7 @@ export const resources = sqliteTable("resources", { skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, { onDelete: "cascade" }), - headers: text("headers"), // comma-separated list of headers to add to the request + headers: text("headers") // comma-separated list of headers to add to the request }); export const targets = sqliteTable("targets", { @@ -129,7 +134,7 @@ export const targets = sqliteTable("targets", { internalPort: integer("internalPort"), enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), path: text("path"), - pathMatchType: text("pathMatchType"), // exact, prefix, regex + pathMatchType: text("pathMatchType") // exact, prefix, regex }); export const exitNodes = sqliteTable("exitNodes", { diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 4ab47ed6..c482a564 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -206,7 +206,7 @@ export async function verifyResourceSession( endpoint = config.getRawConfig().app.dashboard_url!; } const redirectUrl = `${endpoint}/auth/resource/${encodeURIComponent( - resource.resourceId + resource.resourceGuid )}?redirect=${encodeURIComponent(originalRequestURL)}`; // check for access token in headers diff --git a/server/routers/external.ts b/server/routers/external.ts index b851eda8..08b3c119 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -540,7 +540,7 @@ authenticated.post( ); authenticated.post(`/supporter-key/hide`, supporterKey.hideSupporterKey); -unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo); +unauthenticated.get("/resource/:resourceGuid/auth", resource.getResourceAuthInfo); // authenticated.get( // "/role/:roleId/resources", diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index c775564b..006495a7 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -15,16 +15,15 @@ import logger from "@server/logger"; const getResourceAuthInfoSchema = z .object({ - resourceId: z - .string() - .transform(Number) - .pipe(z.number().int().positive()) + resourceGuid: z.string() }) .strict(); export type GetResourceAuthInfoResponse = { resourceId: number; + resourceGuid: string; resourceName: string; + niceId: string; password: boolean; pincode: boolean; sso: boolean; @@ -51,7 +50,7 @@ export async function getResourceAuthInfo( ); } - const { resourceId } = parsedParams.data; + const { resourceGuid } = parsedParams.data; const [result] = await db .select() @@ -64,7 +63,7 @@ export async function getResourceAuthInfo( resourcePassword, eq(resourcePassword.resourceId, resources.resourceId) ) - .where(eq(resources.resourceId, resourceId)) + .where(eq(resources.resourceGuid, resourceGuid)) .limit(1); const resource = result?.resources; @@ -81,6 +80,8 @@ export async function getResourceAuthInfo( return response(res, { data: { + niceId: resource.niceId, + resourceGuid: resource.resourceGuid, resourceId: resource.resourceId, resourceName: resource.name, password: password !== null, diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts index 77df637c..fdd1d0b5 100644 --- a/server/setup/scriptsPg/1.10.4.ts +++ b/server/setup/scriptsPg/1.10.4.ts @@ -1,6 +1,7 @@ import { db } from "@server/db/pg/driver"; import { sql } from "drizzle-orm"; import { isoBase64URL } from "@simplewebauthn/server/helpers"; +import { randomUUID } from "crypto"; const version = "1.10.4"; @@ -10,34 +11,70 @@ export default async function migration() { try { await db.execute(sql`BEGIN`); - const webauthnCredentialsQuery = await db.execute(sql`SELECT "credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated" FROM "webauthnCredentials"`); + const webauthnCredentialsQuery = await db.execute( + sql`SELECT "credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated" FROM "webauthnCredentials"` + ); - const webauthnCredentials = webauthnCredentialsQuery.rows as { - credentialId: string; - publicKey: string; - userId: string; - signCount: number; - transports: string | null; - name: string | null; - lastUsed: string; + const webauthnCredentials = webauthnCredentialsQuery.rows as { + credentialId: string; + publicKey: string; + userId: string; + signCount: number; + transports: string | null; + name: string | null; + lastUsed: string; dateCreated: string; }[]; for (const webauthnCredential of webauthnCredentials) { - const newCredentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); - const newPublicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); - + const newCredentialId = isoBase64URL.fromBuffer( + new Uint8Array( + Buffer.from(webauthnCredential.credentialId, "base64") + ) + ); + const newPublicKey = isoBase64URL.fromBuffer( + new Uint8Array( + Buffer.from(webauthnCredential.publicKey, "base64") + ) + ); + // Delete the old record await db.execute(sql` - DELETE FROM "webauthnCredentials" + DELETE FROM "webauthnCredentials" WHERE "credentialId" = ${webauthnCredential.credentialId} `); - + // Insert the updated record with converted values await db.execute(sql` INSERT INTO "webauthnCredentials" ("credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated") VALUES (${newCredentialId}, ${newPublicKey}, ${webauthnCredential.userId}, ${webauthnCredential.signCount}, ${webauthnCredential.transports}, ${webauthnCredential.name}, ${webauthnCredential.lastUsed}, ${webauthnCredential.dateCreated}) `); + + // 1. Add the column with placeholder so NOT NULL is satisfied + await db.execute(sql` + ALTER TABLE "resources" + ADD COLUMN IF NOT EXISTS "resourceGuid" varchar(36) NOT NULL DEFAULT 'PLACEHOLDER' + `); + + // 2. Fetch every row to backfill UUIDs + const rows = await db.execute( + sql`SELECT "resourceId" FROM "resources" WHERE "resourceGuid" = 'PLACEHOLDER'` + ); + const resources = rows.rows as { resourceId: number }[]; + + for (const r of resources) { + await db.execute(sql` + UPDATE "resources" + SET "resourceGuid" = ${randomUUID()} + WHERE "resourceId" = ${r.resourceId} + `); + } + + // 3. Add UNIQUE constraint now that values are filled + await db.execute(sql` + ALTER TABLE "resources" + ADD CONSTRAINT "resources_resourceGuid_unique" UNIQUE("resourceGuid") + `); } await db.execute(sql`COMMIT`); diff --git a/server/setup/scriptsPg/1.8.0.ts b/server/setup/scriptsPg/1.8.0.ts index 7c0b181b..f3b6c613 100644 --- a/server/setup/scriptsPg/1.8.0.ts +++ b/server/setup/scriptsPg/1.8.0.ts @@ -17,7 +17,7 @@ export default async function migration() { ALTER TABLE "sites" ADD COLUMN "remoteSubnets" text; ALTER TABLE "user" ADD COLUMN "termsAcceptedTimestamp" varchar; ALTER TABLE "user" ADD COLUMN "termsVersion" varchar; - + COMMIT; `); diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts index cd00a65e..912c6fd5 100644 --- a/server/setup/scriptsSqlite/1.10.4.ts +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -2,6 +2,7 @@ import { APP_PATH } from "@server/lib/consts"; import Database from "better-sqlite3"; import path from "path"; import { isoBase64URL } from "@simplewebauthn/server/helpers"; +import { randomUUID } from "crypto"; const version = "1.10.4"; @@ -11,34 +12,77 @@ export default async function migration() { const location = path.join(APP_PATH, "db", "db.sqlite"); const db = new Database(location); - db.transaction(() => { - - const webauthnCredentials = db.prepare(`SELECT credentialId, publicKey, userId, signCount, transports, name, lastUsed, dateCreated FROM 'webauthnCredentials'`).all() as { - credentialId: string; publicKey: string; userId: string; signCount: number; transports: string | null; name: string | null; lastUsed: string; dateCreated: string; + db.transaction(() => { + const webauthnCredentials = db + .prepare( + `SELECT credentialId, publicKey, userId, signCount, transports, name, lastUsed, dateCreated FROM 'webauthnCredentials'` + ) + .all() as { + credentialId: string; + publicKey: string; + userId: string; + signCount: number; + transports: string | null; + name: string | null; + lastUsed: string; + dateCreated: string; }[]; for (const webauthnCredential of webauthnCredentials) { - const newCredentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64'))); - const newPublicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64'))); - + const newCredentialId = isoBase64URL.fromBuffer( + new Uint8Array( + Buffer.from(webauthnCredential.credentialId, "base64") + ) + ); + const newPublicKey = isoBase64URL.fromBuffer( + new Uint8Array( + Buffer.from(webauthnCredential.publicKey, "base64") + ) + ); + // Delete the old record - db.prepare(`DELETE FROM 'webauthnCredentials' WHERE 'credentialId' = ?`).run(webauthnCredential.credentialId); - + db.prepare( + `DELETE FROM 'webauthnCredentials' WHERE 'credentialId' = ?` + ).run(webauthnCredential.credentialId); + // Insert the updated record with converted values db.prepare( `INSERT INTO 'webauthnCredentials' (credentialId, publicKey, userId, signCount, transports, name, lastUsed, dateCreated) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` ).run( - newCredentialId, - newPublicKey, - webauthnCredential.userId, - webauthnCredential.signCount, - webauthnCredential.transports, - webauthnCredential.name, - webauthnCredential.lastUsed, + newCredentialId, + newPublicKey, + webauthnCredential.userId, + webauthnCredential.signCount, + webauthnCredential.transports, + webauthnCredential.name, + webauthnCredential.lastUsed, webauthnCredential.dateCreated ); } - })(); + + // 1. Add the column (nullable or with placeholder) if it doesn’t exist yet + db.prepare( + `ALTER TABLE resources ADD COLUMN resourceGuid TEXT DEFAULT 'PLACEHOLDER';` + ).run(); + + db.prepare( + `CREATE UNIQUE INDEX resources_resourceGuid_unique ON resources ('resourceGuid');` + ).run(); + + // 2. Select all rows + const rows = db.prepare(`SELECT resourceId FROM resources`).all() as { + resourceId: number; + }[]; + + // 3. Prefill with random UUIDs + const updateStmt = db.prepare( + `UPDATE resources SET resourceGuid = ? WHERE resourceId = ?` + ); + + for (const row of rows) { + updateStmt.run(randomUUID(), row.resourceId); + } + })(); console.log(`${version} migration complete`); } diff --git a/src/app/auth/resource/[resourceId]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx similarity index 95% rename from src/app/auth/resource/[resourceId]/page.tsx rename to src/app/auth/resource/[resourceGuid]/page.tsx index 25580ee7..b221f44a 100644 --- a/src/app/auth/resource/[resourceId]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -20,7 +20,7 @@ import AutoLoginHandler from "@app/components/AutoLoginHandler"; export const dynamic = "force-dynamic"; export default async function ResourceAuthPage(props: { - params: Promise<{ resourceId: number }>; + params: Promise<{ resourceGuid: number }>; searchParams: Promise<{ redirect: string | undefined; token: string | undefined; @@ -37,7 +37,7 @@ export default async function ResourceAuthPage(props: { try { const res = await internal.get< AxiosResponse - >(`/resource/${params.resourceId}/auth`, authHeader); + >(`/resource/${params.resourceGuid}/auth`, authHeader); if (res && res.status === 200) { authInfo = res.data.data; @@ -48,10 +48,8 @@ export default async function ResourceAuthPage(props: { const user = await getUser({ skipCheckVerifyEmail: true }); if (!authInfo) { - // TODO: fix this return (
- {/* @ts-ignore */}
); @@ -86,7 +84,7 @@ export default async function ResourceAuthPage(props: { if (user && !user.emailVerified && env.flags.emailVerificationRequired) { redirect( - `/auth/verify-email?redirect=/auth/resource/${authInfo.resourceId}` + `/auth/verify-email?redirect=/auth/resource/${authInfo.resourceGuid}` ); } @@ -103,7 +101,7 @@ export default async function ResourceAuthPage(props: { const res = await priv.post< AxiosResponse >( - `/resource/${params.resourceId}/get-exchange-token`, + `/resource/${authInfo.resourceId}/get-exchange-token`, {}, await authCookieHeader() ); @@ -132,7 +130,7 @@ export default async function ResourceAuthPage(props: {
); From be2b2c6c77cb94d2c66d810cf78453c531e8c0c8 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 16:43:42 -0700 Subject: [PATCH 054/322] add robots.txt --- src/app/robots.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/app/robots.ts diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 00000000..c206249d --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,10 @@ +import type { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + disallow: "/" + } + }; +} From bf993d04f1fa5235f22732ccd42f25407252edda Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 18:07:53 -0700 Subject: [PATCH 055/322] Fix FOU-106 --- server/routers/olm/peers.ts | 6 +++--- server/routers/traefik/getTraefikConfig.ts | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/server/routers/olm/peers.ts b/server/routers/olm/peers.ts index c47c84a8..a8d6be9c 100644 --- a/server/routers/olm/peers.ts +++ b/server/routers/olm/peers.ts @@ -24,7 +24,7 @@ export async function addPeer( throw new Error(`Olm with ID ${clientId} not found`); } - sendToClient(olm.olmId, { + await sendToClient(olm.olmId, { type: "olm/wg/peer/add", data: { siteId: peer.siteId, @@ -49,7 +49,7 @@ export async function deletePeer(clientId: number, siteId: number, publicKey: st throw new Error(`Olm with ID ${clientId} not found`); } - sendToClient(olm.olmId, { + await sendToClient(olm.olmId, { type: "olm/wg/peer/remove", data: { publicKey, @@ -80,7 +80,7 @@ export async function updatePeer( throw new Error(`Olm with ID ${clientId} not found`); } - sendToClient(olm.olmId, { + await sendToClient(olm.olmId, { type: "olm/wg/peer/update", data: { siteId: peer.siteId, diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 5101de84..4bbb13d3 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -352,12 +352,17 @@ export async function getTraefikConfig( if (resource.path && resource.pathMatchType) { priority += 1; // add path to rule based on match type + let path = resource.path; + // if the path doesn't start with a /, add it + if (!path.startsWith("/")) { + path = `/${path}`; + } if (resource.pathMatchType === "exact") { - rule += ` && Path(\`${resource.path}\`)`; + rule += ` && Path(\`${path}\`)`; } else if (resource.pathMatchType === "prefix") { - rule += ` && PathPrefix(\`${resource.path}\`)`; + rule += ` && PathPrefix(\`${path}\`)`; } else if (resource.pathMatchType === "regex") { - rule += ` && PathRegexp(\`${resource.path}\`)`; + rule += ` && PathRegexp(\`${resource.path}\`)`; // this is the raw path because it's a regex } } From 0167b30bf15829e7ee7b1f8ed5c9b7e433823fa7 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 18:18:03 -0700 Subject: [PATCH 056/322] Fixes PAN-122 --- install/main.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/install/main.go b/install/main.go index 0b791959..f16dc214 100644 --- a/install/main.go +++ b/install/main.go @@ -59,6 +59,7 @@ type SupportedContainer string const ( Docker SupportedContainer = "docker" Podman SupportedContainer = "podman" + Undefined SupportedContainer = "undefined" ) func main() { @@ -85,6 +86,7 @@ func main() { reader := bufio.NewReader(os.Stdin) var config Config + var alreadyInstalled = false // check if there is already a config file if _, err := os.Stat("config/config.yml"); err != nil { @@ -168,6 +170,7 @@ func main() { } } else { + alreadyInstalled = true fmt.Println("Looks like you already installed Pangolin!") } @@ -227,7 +230,7 @@ func main() { } } - if !config.HybridMode { + if !config.HybridMode && !alreadyInstalled { // Setup Token Section fmt.Println("\n=== Setup Token ===") @@ -563,28 +566,30 @@ func showSetupTokenInstructions(containerType SupportedContainer, dashboardDomai fmt.Println("\n=== Setup Token Instructions ===") fmt.Println("To get your setup token, you need to:") fmt.Println("") - fmt.Println("1. Start the containers:") + fmt.Println("1. Start the containers") if containerType == Docker { - fmt.Println(" docker-compose up -d") - } else { + fmt.Println(" docker compose up -d") + } else if containerType == Podman { fmt.Println(" podman-compose up -d") + } else { } fmt.Println("") fmt.Println("2. Wait for the Pangolin container to start and generate the token") fmt.Println("") - fmt.Println("3. Check the container logs for the setup token:") + fmt.Println("3. Check the container logs for the setup token") if containerType == Docker { fmt.Println(" docker logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'") - } else { + } else if containerType == Podman { fmt.Println(" podman logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'") + } else { } fmt.Println("") - fmt.Println("4. Look for output like:") + fmt.Println("4. Look for output like") fmt.Println(" === SETUP TOKEN GENERATED ===") fmt.Println(" Token: [your-token-here]") fmt.Println(" Use this token on the initial setup page") fmt.Println("") - fmt.Println("5. Use the token to complete initial setup at:") + fmt.Println("5. Use the token to complete initial setup at") fmt.Printf(" https://%s/auth/initial-setup\n", dashboardDomain) fmt.Println("") fmt.Println("The setup token is required to register the first admin account.") From 1bff9f550eadd2bfca8ff9558f3a68ff9974ce3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 01:20:51 +0000 Subject: [PATCH 057/322] Bump the dev-patch-updates group with 3 updates Bumps the dev-patch-updates group with 3 updates: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react), [drizzle-kit](https://github.com/drizzle-team/drizzle-orm) and [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email). Updates `@types/react` from 19.1.13 to 19.1.15 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `drizzle-kit` from 0.31.4 to 0.31.5 - [Release notes](https://github.com/drizzle-team/drizzle-orm/releases) - [Commits](https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.31.4...drizzle-kit@0.31.5) Updates `react-email` from 4.2.11 to 4.2.12 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/react-email/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/react-email@4.2.12/packages/react-email) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.1.15 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: drizzle-kit dependency-version: 0.31.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react-email dependency-version: 4.2.12 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 24 ++++++++++++------------ package.json | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ca0a182..679a9af3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,17 +112,17 @@ "@types/node": "24.5.2", "@types/nodemailer": "7.0.1", "@types/pg": "8.15.5", - "@types/react": "19.1.13", + "@types/react": "19.1.15", "@types/react-dom": "19.1.9", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", "@types/yargs": "17.0.33", - "drizzle-kit": "0.31.4", + "drizzle-kit": "0.31.5", "esbuild": "0.25.10", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.2.11", + "react-email": "4.2.12", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", "tsx": "4.20.6", @@ -6478,9 +6478,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz", - "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==", + "version": "19.1.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.15.tgz", + "integrity": "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -8518,9 +8518,9 @@ } }, "node_modules/drizzle-kit": { - "version": "0.31.4", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz", - "integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==", + "version": "0.31.5", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.5.tgz", + "integrity": "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg==", "dev": true, "license": "MIT", "dependencies": { @@ -16030,9 +16030,9 @@ "license": "0BSD" }, "node_modules/react-email": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.11.tgz", - "integrity": "sha512-/7TXRgsTrXcV1u7kc5ZXDVlPvZqEBaYcflMhE2FgWIJh3OHLjj2FqctFTgYcp0iwzbR59a7gzJLmSKyD0wYJEQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.12.tgz", + "integrity": "sha512-Cp6nYAG9uL4//X/96wDSKxGqVTXsaRMuD1ub0G0iBcfTqLel4VVre3kicq7RraplDuj+ATlqSZu47d7P2ULP5w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 16c28214..65853003 100644 --- a/package.json +++ b/package.json @@ -129,17 +129,17 @@ "@types/node": "24.5.2", "@types/nodemailer": "7.0.1", "@types/pg": "8.15.5", - "@types/react": "19.1.13", + "@types/react": "19.1.15", "@types/react-dom": "19.1.9", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", "@types/yargs": "17.0.33", - "drizzle-kit": "0.31.4", + "drizzle-kit": "0.31.5", "esbuild": "0.25.10", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.2.11", + "react-email": "4.2.12", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", "tsx": "4.20.6", From d387fa3bfb5524666d94fcc65ce3fa4409d662e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 01:21:37 +0000 Subject: [PATCH 058/322] Bump @dotenvx/dotenvx in the dev-minor-updates group Bumps the dev-minor-updates group with 1 update: [@dotenvx/dotenvx](https://github.com/dotenvx/dotenvx). Updates `@dotenvx/dotenvx` from 1.49.1 to 1.51.0 - [Release notes](https://github.com/dotenvx/dotenvx/releases) - [Changelog](https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md) - [Commits](https://github.com/dotenvx/dotenvx/compare/v1.49.1...v1.51.0) --- updated-dependencies: - dependency-name: "@dotenvx/dotenvx" dependency-version: 1.51.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ca0a182..18a66b74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,7 +97,7 @@ "zod-validation-error": "3.5.2" }, "devDependencies": { - "@dotenvx/dotenvx": "1.49.1", + "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", "@tailwindcss/postcss": "^4.1.13", "@types/better-sqlite3": "7.6.12", @@ -1027,9 +1027,9 @@ } }, "node_modules/@dotenvx/dotenvx": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.49.1.tgz", - "integrity": "sha512-LQ8cem3RU/mI2iz5Sy+ypnhfhVge3bc9tsLJg5rdf7j7u1RRTfmmSdLwSjeYI7sL9ToN7rgFkOGSBJqaBT+gSQ==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.51.0.tgz", + "integrity": "sha512-CbMGzyOYSyFF7d4uaeYwO9gpSBzLTnMmSmTVpCZjvpJFV69qYbjYPpzNnCz1mb2wIvEhjWjRwQWuBzTO0jITww==", "dev": true, "license": "BSD-3-Clause", "dependencies": { diff --git a/package.json b/package.json index 16c28214..2e7f99a0 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "zod-validation-error": "3.5.2" }, "devDependencies": { - "@dotenvx/dotenvx": "1.49.1", + "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", "@tailwindcss/postcss": "^4.1.13", "@types/better-sqlite3": "7.6.12", From db513b43e71949a2d5b15d54d501dc8a98e0bd69 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 18:21:56 -0700 Subject: [PATCH 059/322] Make postgres connection string also a ENV var --- server/db/pg/driver.ts | 19 ++++++++++++++++--- server/lib/readConfigFile.ts | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/server/db/pg/driver.ts b/server/db/pg/driver.ts index c7c292f0..34d16aa1 100644 --- a/server/db/pg/driver.ts +++ b/server/db/pg/driver.ts @@ -7,9 +7,22 @@ function createDb() { const config = readConfigFile(); if (!config.postgres) { - throw new Error( - "Postgres configuration is missing in the configuration file." - ); + // check the environment variables for postgres config + if (process.env.POSTGRES_CONNECTION_STRING) { + config.postgres = { + connection_string: process.env.POSTGRES_CONNECTION_STRING + }; + if (process.env.POSTGRES_REPLICA_CONNECTION_STRINGS) { + const replicas = process.env.POSTGRES_REPLICA_CONNECTION_STRINGS.split(",").map((conn) => ({ + connection_string: conn.trim() + })); + config.postgres.replicas = replicas; + } + } else { + throw new Error( + "Postgres configuration is missing in the configuration file." + ); + } } const connectionString = config.postgres?.connection_string; diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index 918fa4c4..4ae80ac2 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -150,7 +150,7 @@ export const configSchema = z }), postgres: z .object({ - connection_string: z.string(), + connection_string: z.string().optional(), replicas: z .array( z.object({ From 5797144083cfcd5a324cccceb98392be0ab00a68 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 20:40:18 -0700 Subject: [PATCH 060/322] add favicon back --- src/app/favicon.ico | Bin 0 -> 15406 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/app/favicon.ico diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bcaab339d8dd0c5be5e558c11e1af040a73e72e0 GIT binary patch literal 15406 zcmeI3e~=tS6~`y?TR({T}d);do(~PZ*U3M|JcNr&cZ5Rg{hSAmKzwf=PVJxL=zx~F)zt=Dx z?J$hJsY4T5_~qOZ{nEB$=L+7wQn|BlY|pgq9+$SgUQ>CWouSG*Pevr8ym{2$lv1TH zLg$Y>mm}9Gw3g0P>&+;g>;mUK8QUEiufHkME=p%zcA-a)Cw`f{_x0E|%DpXZl@1W> zX=|t}ZI>2NZ!Nr5@6os6-DaONL#SkI=a)6@m2=Yshl!`%`brt0M6bys9b7=bn_ouj4AiKXr+vn_&gTigX&!!rCUs}08%qaKnXuHb0a{rK; z?{-Q%$~w%xSKg_gt>OXT{b#!1b|bg>mHM^gC31fQoNK{%xwM_NXn0q~Rxp=ms**Xk zue=R7{$4{vIsZXtzMEFw85z6uVZ$g-GcE6PQWsh`Qh!rt!Aog8z3vAx%Gppu1K!kK zNgwZ%@f!c`Wb$r57*+{Q(N)1=+Rn$Rw)e`QmJF-U7z4hID949cF0luj8bFo#@-ApfxXLIf_31q(VEi0<68kijUpikr5M3!ug+{Awkv7=_+ z$Skq#nS%TNaozoK)i^>Iz0x*24g>Oe7jk+%aUYd?Ya<&1tqyF~(h5!G{!i?39RG}6 zJiJn`feg@*>zbKktjz^zvu=s4b{`b}$nEgl;_?mw7UuBj$@n{~x~%87585TYBm0E` z{jBBf2aaoy_xFSLGB&~YN$+{*n?ajq@5t?Xp^dCBfcLL>-o$tuEp&LlZc@JEVMnvb+z%`$tLmoA{Cez2@$|4Ki)_H2AE`Sne+Ze)N3}{kl1L zpX`nduV{|1Uz>Sw*{nt7U1zBEJM&zG$GMa7sryYiZZ1DIyBQ4Pk3erDa$h3txZjqt z-9zj0XgvScAirb8*9&mW>G{XDrd2vh){mepxFa*)m&OOwexL0gul+>ZTh}TE-40nW zZcp<3H`YZ@`W0Pk(yF*1%)_)tW{Ry7Y~Z>u{H^6@>>6#a78wM6mb=7AloR7j#64!- zIXI}JV;toUhQHT)Mn|cKEmiKL{+PsZmH0e`vse&qzd zdid`{5ySUMId(HGlk7>^XA$U8HT(enwn-?E1st`>T8d z!wpg1!Mpa+m3wC#jW(5S%GkriUH*@8{F<5fzN+I3e1>dcFxO!Xq>;{U_1IU~FRAv;_-tem$G_J`?2~y!+$sT&!b_ zW@Xx-zVJ@x-KzK{;2XVX3nMw~#l^G<;_9jPe;`$G4~_C^_)hF3F+B6JpX*94Svwx! z{s?1*-{36g`JPF~i#oXzN3sohwNN2bD9wd;yo{LV*w+^QlLlari`aB{SFI=L%?M?8 zFm8u8;}bXFR`t14jqTybxH>Le1+ROszn7$J=g5wpft{)IxV?Y$&r}7&cRvi4L0J_DZ6-3 z&MNNZ`zc9t5ABf)KHZVbkC&K}o4~Rpj;dfwDd#Ne+>cN9EO*IMzREQ)ey!#HBJH2T z*4-ufCUgFVSyA6Mj&{psJ>{)*46fV~ytVDm^t#9T^vcsDH!D6rV*=)u{DV=k-D&cx zItLx&hqa6y9}i52Aj=m5>~fcN#Oy22!0+kjot%1td~Aa?6*}U_t{^Yj-(?N8Wo|*Q zL)+JgT(ipg7~_v$>Q%;sx>H@3gZ8Dglon0HsKdPXjR&BZJx2KVr>@7w0mH5niF{6e*QV(ym z+_XKLx#>+2zwDfK2i;JtH@(RDF=9hPrwHjXJZmQ-Q>!x=}xNh7tZOARl`dQVsnK`^s;_)8Vy*TAvyoq^9FUkDNIxEJ(CZsvJ~F6?_a*C#afK0{3`l6WizC3a}1KO=cPf32)wctGSs{!jLF zUY5PYuq@b@`XuEwHh{8*I>a1XiC>}j&cigP`s`z>#NSS*8u}1(JK68#?(aP|hq3;65XXoPHQG;3 z2;Gp}A386-#9CsreWaa#e#Tp^bENh1T~q7NYB_6UBv^lgGfQ#((Yby9{E(Ix@yz*B zPjtvs?)_SqZ09%ht-d^4{3d$HICIX2^GwlxNIZ=%nX$YZC)=~c?>_GjV!PYebN4sQ#*kAt>+Oem-tGb;d~mN zjHO;*g#R1JQ1%(I9}<%~kmhPn~i_t&%F1HTyAY`L;w-49!A12~w|ve#b^c2&85PFP}_ ziM4Ob;jYyuc(ay09GpQ8uu41-JV);m6KC09rQdpg|IHl>Z$!sM5kG{vdIs};89H`3 zbLuoO9>;Zv=$+WRj{f3aoJl!~dU9qXh}&Z2vswFx3_C0@9hEUWiQ)Cy+g=~Kt-9uT zat1SsJ$M(JMnCS_tnzEUKPA&@e5Np$FHdLPyp;L=`dg?UZ?HyOJ3S`97RWw)*JsXH z_rWhdOkMwD3-u3uZY_r=el<4dJo^45Sl^6!QG)+9@NPZ-$QmYbXUcNFB70xxRpea5 z=33X7tG07y-7{=qg-QI(nH8Nw72T4&ygo0#3i-&suI%LwAg}fM^7oo3lmB8QU;7wq z=AHAebfI`eVpuI!l)Qr2;xzxmk$seuJ$$ICh7Z&K*Wo`xsY3CiwIsfFJ2A%5lH4iS+52(o?N`eBEq?8Docp?3 z`WY8L#?ju~Wu1M)d!q07`cBmE7R=J06`WVOf<3hp#4c=ZJc;S!Po?CXq;j7DzhIZS fyOL+wFF9B8H~KsWv6RGGiRr#w@ofog0SWvchs=Li literal 0 HcmV?d00001 From a30222a13ed00c9c942c417ad8cc3cf3edc96023 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 28 Sep 2025 22:18:18 -0700 Subject: [PATCH 061/322] add templates --- .../DISCUSSION_TEMPLATE/feature-requests.yml | 64 +++++++++++++++++++ .github/ISSUE_TEMPLATE/1.bug_report.yml | 50 +++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 +++ 3 files changed, 122 insertions(+) create mode 100644 .github/DISCUSSION_TEMPLATE/feature-requests.yml create mode 100644 .github/ISSUE_TEMPLATE/1.bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/DISCUSSION_TEMPLATE/feature-requests.yml b/.github/DISCUSSION_TEMPLATE/feature-requests.yml new file mode 100644 index 00000000..f503f8cd --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/feature-requests.yml @@ -0,0 +1,64 @@ +body: + - type: textarea + attributes: + label: Summary + description: A clear and concise summary of the requested feature. + validations: + required: true + + - type: textarea + attributes: + label: Motivation + description: | + Why is this feature important? + Explain the problem this feature would solve or what use case it would enable. + validations: + required: true + + - type: textarea + attributes: + label: Proposed Solution + description: | + How would you like to see this feature implemented? + Provide as much detail as possible about the desired behavior, configuration, or changes. + validations: + required: true + + - type: textarea + attributes: + label: Alternatives Considered + description: Describe any alternative solutions or workarounds you've thought about. + validations: + required: false + + - type: checkboxes + attributes: + label: Scope + description: Which parts of the system does this feature affect? + options: + - label: Pangolin Core + - label: Gerbil + - label: Traefik Integration + - label: Newt + - label: Deployment Tooling + - label: Authentication + - label: UI/UX + - label: Advanced Networking + - label: Security + - label: Performance + - label: Other + + - type: textarea + attributes: + label: Additional Context + description: Add any other context, mockups, or screenshots about the feature request here. + validations: + required: false + + - type: markdown + attributes: + value: | + Before submitting, please: + - Check if there is an existing issue for this feature. + - Clearly explain the benefit and use case. + - Be as specific as possible to help contributors evaluate and implement. diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 00000000..ea5c1868 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,50 @@ +name: Bug Report +description: Create a bug report for the Pangolin +labels: [] +body: + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + attributes: + label: Environment + description: Please fill out the relevant details below for your environment. + value: | + - OS Type & Version: (e.g., Ubuntu 22.04) + - Pangolin Version: + - Gerbil Version: + - Traefik Version: + - Newt Version: + validations: + required: true + + - type: textarea + attributes: + label: To Reproduce + description: | + Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. + + If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken. + validations: + required: true + + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + + - type: markdown + attributes: + value: | + Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + + - type: markdown + attributes: + value: | + Contributors should be able to follow the steps provided in order to reproduce the bug. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..a3739c4d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Need help or have questions? + url: https://github.com/orgs/fosrl/discussions + about: Ask questions, get help, and discuss with other community members + - name: Request a Feature + url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests + about: Feature requests should be opened as discussions so others can upvote and comment From 4dd9e34a1141cdc54e5f0643d43511ca65938ef0 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 29 Sep 2025 14:46:59 +0530 Subject: [PATCH 062/322] use resourceGuid instead of resourceid --- src/app/[orgId]/settings/resources/[niceId]/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/layout.tsx b/src/app/[orgId]/settings/resources/[niceId]/layout.tsx index 3f8425ce..8c140333 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/layout.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/layout.tsx @@ -46,7 +46,7 @@ export default async function ResourceLayout(props: ResourceLayoutProps) { try { const res = await internal.get< AxiosResponse - >(`/resource/${resource.resourceId}/auth`, await authCookieHeader()); + >(`/resource/${resource.resourceGuid}/auth`, await authCookieHeader()); authInfo = res.data.data; } catch { redirect(`/${params.orgId}/settings/resources`); From e34a31941dc12ad523a6607855f05a45f52e89bc Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 29 Sep 2025 09:54:06 -0700 Subject: [PATCH 063/322] Add org settings column --- server/db/pg/schema.ts | 3 ++- server/db/sqlite/schema.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 5e34b033..94e9d98a 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -25,7 +25,8 @@ export const orgs = pgTable("orgs", { orgId: varchar("orgId").primaryKey(), name: varchar("name").notNull(), subnet: varchar("subnet"), - createdAt: text("createdAt") + createdAt: text("createdAt"), + settings: text("settings") // JSON blob of org-specific settings }); export const orgDomains = pgTable("orgDomains", { diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 58827b00..0fb65d03 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -18,7 +18,8 @@ export const orgs = sqliteTable("orgs", { orgId: text("orgId").primaryKey(), name: text("name").notNull(), subnet: text("subnet"), - createdAt: text("createdAt") + createdAt: text("createdAt"), + settings: text("settings") // JSON blob of org-specific settings }); export const userDomains = sqliteTable("userDomains", { From dca0fb327b7887c226fde5236342ae4c79a9507d Mon Sep 17 00:00:00 2001 From: nicolaus-hee <48563755+nicolaus-hee@users.noreply.github.com> Date: Mon, 29 Sep 2025 19:41:24 +0200 Subject: [PATCH 064/322] Update de-DE.json: Server-Administrator->Administration Menu item "Server Admin" --> "Server-Administation" the activity / section not "Server-Administratior" (the person) --- messages/de-DE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index ed5d2042..dcdb1440 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -979,7 +979,7 @@ "supportKeyBuy": "Unterstützer-Schlüssel kaufen", "logoutError": "Fehler beim Abmelden", "signingAs": "Angemeldet als", - "serverAdmin": "Server-Administrator", + "serverAdmin": "Server-Administration", "managedSelfhosted": "Verwaltetes Selbsthosted", "otpEnable": "Zwei-Faktor aktivieren", "otpDisable": "Zwei-Faktor deaktivieren", From baa98952faebb971f8540f07b0ad5aae5dbfb930 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Mon, 29 Sep 2025 11:38:04 -0700 Subject: [PATCH 065/322] Revert "Update de-DE.json: Server-Administrator->Administration" --- messages/de-DE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index dcdb1440..ed5d2042 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -979,7 +979,7 @@ "supportKeyBuy": "Unterstützer-Schlüssel kaufen", "logoutError": "Fehler beim Abmelden", "signingAs": "Angemeldet als", - "serverAdmin": "Server-Administration", + "serverAdmin": "Server-Administrator", "managedSelfhosted": "Verwaltetes Selbsthosted", "otpEnable": "Zwei-Faktor aktivieren", "otpDisable": "Zwei-Faktor deaktivieren", From a433d975734d5399d7792f5b77d0b462aa14a705 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 29 Sep 2025 14:21:23 -0700 Subject: [PATCH 066/322] Make proxy port optional Fixes #1585 --- src/components/ResourcesTable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ResourcesTable.tsx b/src/components/ResourcesTable.tsx index cb7983c7..7a645bc7 100644 --- a/src/components/ResourcesTable.tsx +++ b/src/components/ResourcesTable.tsx @@ -371,7 +371,7 @@ export default function ResourcesTable({
{!resourceRow.http ? ( ) : !resourceRow.domainId ? ( @@ -547,7 +547,7 @@ export default function ResourcesTable({ const resourceRow = row.original; return ( ); From 032d48e3942c44c658869c594a68763e463659a1 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 29 Sep 2025 16:12:17 -0700 Subject: [PATCH 067/322] add period to cookie --- src/actions/server.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/actions/server.ts b/src/actions/server.ts index 0aea77c7..27d6b562 100644 --- a/src/actions/server.ts +++ b/src/actions/server.ts @@ -2,6 +2,7 @@ import { cookies } from "next/headers"; import { ResponseT } from "@server/types/Response"; +import { pullEnv } from "@app/lib/pullEnv"; type CookieOptions = { path?: string; @@ -10,6 +11,7 @@ type CookieOptions = { sameSite?: "lax" | "strict" | "none"; expires?: Date; maxAge?: number; + domain?: string; }; function parseSetCookieString(setCookie: string): { @@ -22,6 +24,8 @@ function parseSetCookieString(setCookie: string): { const [name, ...valParts] = nameValue.split("="); const value = valParts.join("="); // handles '=' inside JWT + const env = pullEnv(); + const options: CookieOptions = {}; for (const attr of attrParts) { @@ -46,6 +50,18 @@ function parseSetCookieString(setCookie: string): { case "max-age": options.maxAge = parseInt(v, 10); break; + case "domain": + options.domain = v; + break; + } + } + + if (!options.domain) { + const d = env.app.dashboardUrl + ? "." + new URL(env.app.dashboardUrl).hostname + : undefined; + if (d) { + options.domain = d; } } From fec29eb349c94fbe654cb300dfb2885d8e6dfa17 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 29 Sep 2025 16:39:36 -0700 Subject: [PATCH 068/322] update templates --- .../DISCUSSION_TEMPLATE/feature-requests.yml | 17 ----------------- .github/ISSUE_TEMPLATE/1.bug_report.yml | 3 ++- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/feature-requests.yml b/.github/DISCUSSION_TEMPLATE/feature-requests.yml index f503f8cd..03b580ca 100644 --- a/.github/DISCUSSION_TEMPLATE/feature-requests.yml +++ b/.github/DISCUSSION_TEMPLATE/feature-requests.yml @@ -31,23 +31,6 @@ body: validations: required: false - - type: checkboxes - attributes: - label: Scope - description: Which parts of the system does this feature affect? - options: - - label: Pangolin Core - - label: Gerbil - - label: Traefik Integration - - label: Newt - - label: Deployment Tooling - - label: Authentication - - label: UI/UX - - label: Advanced Networking - - label: Security - - label: Performance - - label: Other - - type: textarea attributes: label: Additional Context diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index ea5c1868..41dbe7bf 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -1,5 +1,5 @@ name: Bug Report -description: Create a bug report for the Pangolin +description: Create a bug report labels: [] body: - type: textarea @@ -19,6 +19,7 @@ body: - Gerbil Version: - Traefik Version: - Newt Version: + - Olm Version: (if applicable) validations: required: true From 71bcf257181f5be7c824d709bf6067d32bde4b73 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 29 Sep 2025 17:07:38 -0700 Subject: [PATCH 069/322] move table unique constraint --- server/setup/scriptsSqlite/1.10.4.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts index 912c6fd5..8932a2eb 100644 --- a/server/setup/scriptsSqlite/1.10.4.ts +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -65,10 +65,6 @@ export default async function migration() { `ALTER TABLE resources ADD COLUMN resourceGuid TEXT DEFAULT 'PLACEHOLDER';` ).run(); - db.prepare( - `CREATE UNIQUE INDEX resources_resourceGuid_unique ON resources ('resourceGuid');` - ).run(); - // 2. Select all rows const rows = db.prepare(`SELECT resourceId FROM resources`).all() as { resourceId: number; @@ -82,6 +78,10 @@ export default async function migration() { for (const row of rows) { updateStmt.run(randomUUID(), row.resourceId); } + + db.prepare( + `CREATE UNIQUE INDEX resources_resourceGuid_unique ON resources ('resourceGuid');` + ).run(); })(); console.log(`${version} migration complete`); From d1707801bf955b2ab1834c5d9f407126064cd821 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 03:40:16 +0000 Subject: [PATCH 070/322] Bump the dev-minor-updates group with 2 updates Bumps the dev-minor-updates group with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `@types/node` from 24.5.2 to 24.6.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `typescript-eslint` from 8.44.1 to 8.45.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.45.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 24.6.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: typescript-eslint dependency-version: 8.45.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 140 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0985af1e..97337966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,7 +109,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.5.2", + "@types/node": "24.6.0", "@types/nodemailer": "7.0.1", "@types/pg": "8.15.5", "@types/react": "19.1.15", @@ -127,7 +127,7 @@ "tsc-alias": "1.8.16", "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.44.1" + "typescript-eslint": "^8.45.0" } }, "node_modules/@alloc/quick-lru": { @@ -6431,13 +6431,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", - "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.0.tgz", + "integrity": "sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~7.12.0" + "undici-types": "~7.13.0" } }, "node_modules/@types/nodemailer": { @@ -6579,16 +6579,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", - "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/type-utils": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -6602,7 +6602,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -6617,15 +6617,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", - "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "engines": { @@ -6641,13 +6641,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", - "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.1", - "@typescript-eslint/types": "^8.44.1", + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", "debug": "^4.3.4" }, "engines": { @@ -6662,13 +6662,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", - "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6679,9 +6679,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", - "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6695,14 +6695,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", - "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6719,9 +6719,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", - "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6732,15 +6732,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", - "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.1", - "@typescript-eslint/tsconfig-utils": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6812,15 +6812,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", - "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1" + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6835,12 +6835,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", - "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/types": "8.45.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -17898,16 +17898,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", - "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.44.1", - "@typescript-eslint/parser": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1" + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -17940,9 +17940,9 @@ } }, "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", "devOptional": true, "license": "MIT" }, diff --git a/package.json b/package.json index 003974c8..39b1996b 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.5.2", + "@types/node": "24.6.0", "@types/nodemailer": "7.0.1", "@types/pg": "8.15.5", "@types/react": "19.1.15", @@ -144,7 +144,7 @@ "tsc-alias": "1.8.16", "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.44.1" + "typescript-eslint": "^8.45.0" }, "overrides": { "emblor": { From 411fa9345f7d23eacf7555a063a26e20b6c72446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:25:14 +0000 Subject: [PATCH 071/322] Bump the dev-patch-updates group across 1 directory with 2 updates Bumps the dev-patch-updates group with 2 updates in the / directory: [@types/nodemailer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/nodemailer) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). Updates `@types/nodemailer` from 7.0.1 to 7.0.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/nodemailer) Updates `@types/react` from 19.1.15 to 19.1.16 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/nodemailer" dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@types/react" dependency-version: 19.1.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97337966..72ae8583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110,9 +110,9 @@ "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", "@types/node": "24.6.0", - "@types/nodemailer": "7.0.1", + "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", - "@types/react": "19.1.15", + "@types/react": "19.1.16", "@types/react-dom": "19.1.9", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", @@ -6441,9 +6441,9 @@ } }, "node_modules/@types/nodemailer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.1.tgz", - "integrity": "sha512-UfHAghPmGZVzaL8x9y+mKZMWyHC399+iq0MOmya5tIyenWX3lcdSb60vOmp0DocR6gCDTYTozv/ULQnREyyjkg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.2.tgz", + "integrity": "sha512-Zo6uOA9157WRgBk/ZhMpTQ/iCWLMk7OIs/Q9jvHarMvrzUUP/MDdPHL2U1zpf57HrrWGv4nYQn5uIxna0xY3xw==", "dev": true, "license": "MIT", "dependencies": { @@ -6478,9 +6478,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.15.tgz", - "integrity": "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA==", + "version": "19.1.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz", + "integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 39b1996b..405157b8 100644 --- a/package.json +++ b/package.json @@ -127,9 +127,9 @@ "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", "@types/node": "24.6.0", - "@types/nodemailer": "7.0.1", + "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", - "@types/react": "19.1.15", + "@types/react": "19.1.16", "@types/react-dom": "19.1.9", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", From 089e43e1cecd5e9d5d248a2725c48248ef940278 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 30 Sep 2025 13:59:12 -0700 Subject: [PATCH 072/322] Update migration --- server/setup/scriptsPg/1.10.4.ts | 28 +++++++++++++++------------- server/setup/scriptsSqlite/1.10.4.ts | 2 ++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/server/setup/scriptsPg/1.10.4.ts b/server/setup/scriptsPg/1.10.4.ts index fdd1d0b5..da862412 100644 --- a/server/setup/scriptsPg/1.10.4.ts +++ b/server/setup/scriptsPg/1.10.4.ts @@ -49,33 +49,35 @@ export default async function migration() { INSERT INTO "webauthnCredentials" ("credentialId", "publicKey", "userId", "signCount", "transports", "name", "lastUsed", "dateCreated") VALUES (${newCredentialId}, ${newPublicKey}, ${webauthnCredential.userId}, ${webauthnCredential.signCount}, ${webauthnCredential.transports}, ${webauthnCredential.name}, ${webauthnCredential.lastUsed}, ${webauthnCredential.dateCreated}) `); + } - // 1. Add the column with placeholder so NOT NULL is satisfied - await db.execute(sql` + // 1. Add the column with placeholder so NOT NULL is satisfied + await db.execute(sql` ALTER TABLE "resources" ADD COLUMN IF NOT EXISTS "resourceGuid" varchar(36) NOT NULL DEFAULT 'PLACEHOLDER' `); - // 2. Fetch every row to backfill UUIDs - const rows = await db.execute( - sql`SELECT "resourceId" FROM "resources" WHERE "resourceGuid" = 'PLACEHOLDER'` - ); - const resources = rows.rows as { resourceId: number }[]; + // 2. Fetch every row to backfill UUIDs + const rows = await db.execute( + sql`SELECT "resourceId" FROM "resources" WHERE "resourceGuid" = 'PLACEHOLDER'` + ); + const resources = rows.rows as { resourceId: number }[]; - for (const r of resources) { - await db.execute(sql` + for (const r of resources) { + await db.execute(sql` UPDATE "resources" SET "resourceGuid" = ${randomUUID()} WHERE "resourceId" = ${r.resourceId} `); - } + } - // 3. Add UNIQUE constraint now that values are filled - await db.execute(sql` + // 3. Add UNIQUE constraint now that values are filled + await db.execute(sql` ALTER TABLE "resources" ADD CONSTRAINT "resources_resourceGuid_unique" UNIQUE("resourceGuid") `); - } + + await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN IF NOT EXISTS "settings" text`); await db.execute(sql`COMMIT`); console.log(`Updated credentialId and publicKey`); diff --git a/server/setup/scriptsSqlite/1.10.4.ts b/server/setup/scriptsSqlite/1.10.4.ts index 912c6fd5..63190117 100644 --- a/server/setup/scriptsSqlite/1.10.4.ts +++ b/server/setup/scriptsSqlite/1.10.4.ts @@ -82,6 +82,8 @@ export default async function migration() { for (const row of rows) { updateStmt.run(randomUUID(), row.resourceId); } + + db.prepare(`ALTER TABLE "orgs" ADD COLUMN IF NOT EXISTS "settings" text`).run(); })(); console.log(`${version} migration complete`); From a2dae8aa1359f5474082560f54cea1a3ca7330cb Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 30 Sep 2025 17:34:26 -0700 Subject: [PATCH 073/322] Fix updating sites on exit nodes --- server/routers/client/updateClient.ts | 485 +++++++++++++------------- 1 file changed, 236 insertions(+), 249 deletions(-) diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index 81ee4278..dae80732 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, exitNodes, sites } from "@server/db"; +import { Client, db, exitNodes, sites } from "@server/db"; import { clients, clientSites } from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -105,146 +105,28 @@ export async function updateClient( ); } - if (siteIds) { - let sitesAdded = []; - let sitesRemoved = []; + let sitesAdded = []; + let sitesRemoved = []; - // Fetch existing site associations - const existingSites = await db - .select({ siteId: clientSites.siteId }) - .from(clientSites) - .where(eq(clientSites.clientId, clientId)); + // Fetch existing site associations + const existingSites = await db + .select({ siteId: clientSites.siteId }) + .from(clientSites) + .where(eq(clientSites.clientId, clientId)); - const existingSiteIds = existingSites.map((site) => site.siteId); + const existingSiteIds = existingSites.map((site) => site.siteId); - // Determine which sites were added and removed - sitesAdded = siteIds.filter( - (siteId) => !existingSiteIds.includes(siteId) - ); - sitesRemoved = existingSiteIds.filter( - (siteId) => !siteIds.includes(siteId) - ); - - logger.info( - `Adding ${sitesAdded.length} new sites to client ${client.clientId}` - ); - for (const siteId of sitesAdded) { - if (!client.subnet || !client.pubKey) { - logger.debug( - "Client subnet, pubKey or endpoint is not set" - ); - continue; - } - - // TODO: WE NEED TO HANDLE THIS BETTER. RIGHT NOW WE ARE JUST GUESSING BASED ON THE OTHER SITES - // BUT REALLY WE NEED TO TRACK THE USERS PREFERENCE THAT THEY CHOSE IN THE CLIENTS - const isRelayed = true; - - // get the clientsite - const [clientSite] = await db - .select() - .from(clientSites) - .where( - and( - eq(clientSites.clientId, client.clientId), - eq(clientSites.siteId, siteId) - ) - ) - .limit(1); - - if (!clientSite || !clientSite.endpoint) { - logger.debug("Client site is missing or has no endpoint"); - continue; - } - - const site = await newtAddPeer(siteId, { - publicKey: client.pubKey, - allowedIps: [`${client.subnet.split("/")[0]}/32`], // we want to only allow from that client - endpoint: isRelayed ? "" : clientSite.endpoint - }); - - if (!site) { - logger.debug("Failed to add peer to newt - missing site"); - continue; - } - - if (!site.endpoint || !site.publicKey) { - logger.debug("Site endpoint or publicKey is not set"); - continue; - } - - let endpoint; - - if (isRelayed) { - if (!site.exitNodeId) { - logger.warn( - `Site ${site.siteId} has no exit node, skipping` - ); - return null; - } - - // get the exit node for the site - const [exitNode] = await db - .select() - .from(exitNodes) - .where(eq(exitNodes.exitNodeId, site.exitNodeId)) - .limit(1); - - if (!exitNode) { - logger.warn( - `Exit node not found for site ${site.siteId}` - ); - return null; - } - - endpoint = `${exitNode.endpoint}:21820`; - } else { - if (!endpoint) { - logger.warn( - `Site ${site.siteId} has no endpoint, skipping` - ); - return null; - } - endpoint = site.endpoint; - } - - await olmAddPeer(client.clientId, { - siteId: site.siteId, - endpoint: endpoint, - publicKey: site.publicKey, - serverIP: site.address, - serverPort: site.listenPort, - remoteSubnets: site.remoteSubnets - }); - } - - logger.info( - `Removing ${sitesRemoved.length} sites from client ${client.clientId}` - ); - for (const siteId of sitesRemoved) { - if (!client.pubKey) { - logger.debug("Client pubKey is not set"); - continue; - } - const site = await newtDeletePeer(siteId, client.pubKey); - if (!site) { - logger.debug( - "Failed to delete peer from newt - missing site" - ); - continue; - } - if (!site.endpoint || !site.publicKey) { - logger.debug("Site endpoint or publicKey is not set"); - continue; - } - await olmDeletePeer( - client.clientId, - site.siteId, - site.publicKey - ); - } - } + const siteIdsToProcess = siteIds || []; + // Determine which sites were added and removed + sitesAdded = siteIdsToProcess.filter( + (siteId) => !existingSiteIds.includes(siteId) + ); + sitesRemoved = existingSiteIds.filter( + (siteId) => !siteIdsToProcess.includes(siteId) + ); + let updatedClient: Client | undefined = undefined; + let sitesData: any; // TODO: define type somehow from the query below await db.transaction(async (trx) => { // Update client name if provided if (name) { @@ -255,133 +137,238 @@ export async function updateClient( } // Update site associations if provided - if (siteIds) { - // Delete existing site associations + // Remove sites that are no longer associated + for (const siteId of sitesRemoved) { await trx .delete(clientSites) - .where(eq(clientSites.clientId, clientId)); - - // Create new site associations - if (siteIds.length > 0) { - await trx.insert(clientSites).values( - siteIds.map((siteId) => ({ - clientId, - siteId - })) + .where( + and( + eq(clientSites.clientId, clientId), + eq(clientSites.siteId, siteId) + ) ); - } } - // get all sites for this client and join with exit nodes with site.exitNodeId - const sitesData = await db - .select() - .from(sites) - .innerJoin(clientSites, eq(sites.siteId, clientSites.siteId)) - .leftJoin(exitNodes, eq(sites.exitNodeId, exitNodes.exitNodeId)) - .where(eq(clientSites.clientId, client.clientId)); - - let exitNodeDestinations: { - reachableAt: string; - exitNodeId: number; - type: string; - sourceIp: string; - sourcePort: number; - destinations: PeerDestination[]; - }[] = []; - - for (const site of sitesData) { - if (!site.sites.subnet) { - logger.warn( - `Site ${site.sites.siteId} has no subnet, skipping` - ); - continue; - } - - if (!site.clientSites.endpoint) { - logger.warn( - `Site ${site.sites.siteId} has no endpoint, skipping` - ); - continue; - } - - // find the destinations in the array - let destinations = exitNodeDestinations.find( - (d) => d.reachableAt === site.exitNodes?.reachableAt - ); - - if (!destinations) { - destinations = { - reachableAt: site.exitNodes?.reachableAt || "", - exitNodeId: site.exitNodes?.exitNodeId || 0, - type: site.exitNodes?.type || "", - sourceIp: site.clientSites.endpoint.split(":")[0] || "", - sourcePort: - parseInt(site.clientSites.endpoint.split(":")[1]) || - 0, - destinations: [ - { - destinationIP: site.sites.subnet.split("/")[0], - destinationPort: site.sites.listenPort || 0 - } - ] - }; - } else { - // add to the existing destinations - destinations.destinations.push({ - destinationIP: site.sites.subnet.split("/")[0], - destinationPort: site.sites.listenPort || 0 - }); - } - - // update it in the array - exitNodeDestinations = exitNodeDestinations.filter( - (d) => d.reachableAt !== site.exitNodes?.reachableAt - ); - exitNodeDestinations.push(destinations); - } - - for (const destination of exitNodeDestinations) { - logger.info( - `Updating destinations for exit node at ${destination.reachableAt}` - ); - const payload = { - sourceIp: destination.sourceIp, - sourcePort: destination.sourcePort, - destinations: destination.destinations - }; - logger.info( - `Payload for update-destinations: ${JSON.stringify(payload, null, 2)}` - ); - - // Create an ExitNode-like object for sendToExitNode - const exitNodeForComm = { - exitNodeId: destination.exitNodeId, - type: destination.type, - reachableAt: destination.reachableAt - } as any; // Using 'as any' since we know sendToExitNode will handle this correctly - - await sendToExitNode(exitNodeForComm, { - remoteType: "remoteExitNode/update-destinations", - localPath: "/update-destinations", - method: "POST", - data: payload + // Add new site associations + for (const siteId of sitesAdded) { + await trx.insert(clientSites).values({ + clientId, + siteId }); } // Fetch the updated client - const [updatedClient] = await trx + [updatedClient] = await trx .select() .from(clients) .where(eq(clients.clientId, clientId)) .limit(1); - return response(res, { - data: updatedClient, - success: true, - error: false, - message: "Client updated successfully", - status: HttpCode.OK + // get all sites for this client and join with exit nodes with site.exitNodeId + sitesData = await trx + .select() + .from(sites) + .innerJoin(clientSites, eq(sites.siteId, clientSites.siteId)) + .leftJoin(exitNodes, eq(sites.exitNodeId, exitNodes.exitNodeId)) + .where(eq(clientSites.clientId, client.clientId)); + }); + + logger.info( + `Adding ${sitesAdded.length} new sites to client ${client.clientId}` + ); + for (const siteId of sitesAdded) { + if (!client.subnet || !client.pubKey) { + logger.debug("Client subnet, pubKey or endpoint is not set"); + continue; + } + + // TODO: WE NEED TO HANDLE THIS BETTER. WE ARE DEFAULTING TO RELAYING FOR NEW SITES + // BUT REALLY WE NEED TO TRACK THE USERS PREFERENCE THAT THEY CHOSE IN THE CLIENTS + // AND TRIGGER A HOLEPUNCH OR SOMETHING TO GET THE ENDPOINT AND HP TO THE NEW SITES + const isRelayed = true; + + const site = await newtAddPeer(siteId, { + publicKey: client.pubKey, + allowedIps: [`${client.subnet.split("/")[0]}/32`], // we want to only allow from that client + // endpoint: isRelayed ? "" : clientSite.endpoint + endpoint: isRelayed ? "" : "" // we are not HPing yet so no endpoint }); + + if (!site) { + logger.debug("Failed to add peer to newt - missing site"); + continue; + } + + if (!site.endpoint || !site.publicKey) { + logger.debug("Site endpoint or publicKey is not set"); + continue; + } + + let endpoint; + + if (isRelayed) { + if (!site.exitNodeId) { + logger.warn( + `Site ${site.siteId} has no exit node, skipping` + ); + return null; + } + + // get the exit node for the site + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, site.exitNodeId)) + .limit(1); + + if (!exitNode) { + logger.warn(`Exit node not found for site ${site.siteId}`); + return null; + } + + endpoint = `${exitNode.endpoint}:21820`; + } else { + if (!site.endpoint) { + logger.warn( + `Site ${site.siteId} has no endpoint, skipping` + ); + return null; + } + endpoint = site.endpoint; + } + + await olmAddPeer(client.clientId, { + siteId: site.siteId, + endpoint: endpoint, + publicKey: site.publicKey, + serverIP: site.address, + serverPort: site.listenPort, + remoteSubnets: site.remoteSubnets + }); + } + + logger.info( + `Removing ${sitesRemoved.length} sites from client ${client.clientId}` + ); + for (const siteId of sitesRemoved) { + if (!client.pubKey) { + logger.debug("Client pubKey is not set"); + continue; + } + const site = await newtDeletePeer(siteId, client.pubKey); + if (!site) { + logger.debug("Failed to delete peer from newt - missing site"); + continue; + } + if (!site.endpoint || !site.publicKey) { + logger.debug("Site endpoint or publicKey is not set"); + continue; + } + await olmDeletePeer(client.clientId, site.siteId, site.publicKey); + } + + if (!updatedClient || !sitesData) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `Failed to update client` + ) + ); + } + + let exitNodeDestinations: { + reachableAt: string; + exitNodeId: number; + type: string; + sourceIp: string; + sourcePort: number; + destinations: PeerDestination[]; + }[] = []; + + for (const site of sitesData) { + if (!site.sites.subnet) { + logger.warn( + `Site ${site.sites.siteId} has no subnet, skipping` + ); + continue; + } + + if (!site.clientSites.endpoint) { + logger.warn( + `Site ${site.sites.siteId} has no endpoint, skipping` + ); + continue; + } + + // find the destinations in the array + let destinations = exitNodeDestinations.find( + (d) => d.reachableAt === site.exitNodes?.reachableAt + ); + + if (!destinations) { + destinations = { + reachableAt: site.exitNodes?.reachableAt || "", + exitNodeId: site.exitNodes?.exitNodeId || 0, + type: site.exitNodes?.type || "", + sourceIp: site.clientSites.endpoint.split(":")[0] || "", + sourcePort: + parseInt(site.clientSites.endpoint.split(":")[1]) || 0, + destinations: [ + { + destinationIP: site.sites.subnet.split("/")[0], + destinationPort: site.sites.listenPort || 0 + } + ] + }; + } else { + // add to the existing destinations + destinations.destinations.push({ + destinationIP: site.sites.subnet.split("/")[0], + destinationPort: site.sites.listenPort || 0 + }); + } + + // update it in the array + exitNodeDestinations = exitNodeDestinations.filter( + (d) => d.reachableAt !== site.exitNodes?.reachableAt + ); + exitNodeDestinations.push(destinations); + } + + for (const destination of exitNodeDestinations) { + logger.info( + `Updating destinations for exit node at ${destination.reachableAt}` + ); + const payload = { + sourceIp: destination.sourceIp, + sourcePort: destination.sourcePort, + destinations: destination.destinations + }; + logger.info( + `Payload for update-destinations: ${JSON.stringify(payload, null, 2)}` + ); + + // Create an ExitNode-like object for sendToExitNode + const exitNodeForComm = { + exitNodeId: destination.exitNodeId, + type: destination.type, + reachableAt: destination.reachableAt + } as any; // Using 'as any' since we know sendToExitNode will handle this correctly + + await sendToExitNode(exitNodeForComm, { + remoteType: "remoteExitNode/update-destinations", + localPath: "/update-destinations", + method: "POST", + data: payload + }); + } + + return response(res, { + data: updatedClient, + success: true, + error: false, + message: "Client updated successfully", + status: HttpCode.OK }); } catch (error) { logger.error(error); From 70914e836f199b141852343f34353c26bc704349 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 30 Sep 2025 21:46:44 -0700 Subject: [PATCH 074/322] Add headers description --- messages/en-US.json | 3 ++- src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/messages/en-US.json b/messages/en-US.json index 94e0266c..d9a60837 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Custom Headers", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Headers must be in the format: Header-Name: value.", "domainPickerProvidedDomain": "Provided Domain", "domainPickerFreeProvidedDomain": "Free Provided Domain", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Edit file: docker-compose.yml", "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." -} +} \ No newline at end of file diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 4c2eedf5..1232e542 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -1489,6 +1489,11 @@ export default function ReverseProxyTargets(props: { rows={4} /> + + {t( + "customHeadersDescription" + )} + )} From 149a4b916b37ff0eb1f5fa496a9f6dccdb2cddcf Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 28 Sep 2025 11:25:11 +0530 Subject: [PATCH 075/322] basic setup for rewriting requests to another path --- messages/en-US.json | 6 +- server/db/pg/schema.ts | 4 +- server/db/sqlite/schema.ts | 4 +- server/routers/target/createTarget.ts | 4 +- server/routers/target/listTargets.ts | 4 +- server/routers/target/updateTarget.ts | 4 +- server/routers/traefik/getTraefikConfig.ts | 392 +++++++++++++++--- .../resources/[niceId]/proxy/page.tsx | 284 +++++++++---- 8 files changed, 557 insertions(+), 145 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index d9a60837..f4e063ce 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edit file: docker-compose.yml", "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", - "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here." -} \ No newline at end of file + "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 94e9d98a..18b29f35 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -123,7 +123,9 @@ export const targets = pgTable("targets", { internalPort: integer("internalPort"), enabled: boolean("enabled").notNull().default(true), path: text("path"), - pathMatchType: text("pathMatchType") // exact, prefix, regex + pathMatchType: text("pathMatchType"), // exact, prefix, regex + rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target + rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix }); export const exitNodes = pgTable("exitNodes", { diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 0fb65d03..e38d6b67 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -135,7 +135,9 @@ export const targets = sqliteTable("targets", { internalPort: integer("internalPort"), enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), path: text("path"), - pathMatchType: text("pathMatchType") // exact, prefix, regex + pathMatchType: text("pathMatchType"), // exact, prefix, regex + rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target + rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix }); export const exitNodes = sqliteTable("exitNodes", { diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index fb85f566..6ed9d9aa 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -32,7 +32,9 @@ const createTargetSchema = z port: z.number().int().min(1).max(65535), enabled: z.boolean().default(true), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable() + pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() // NEW: rewrite path type }) .strict(); diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index ca1159d2..4a1d99a0 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -46,7 +46,9 @@ function queryTargets(resourceId: number) { siteId: targets.siteId, siteType: sites.type, path: targets.path, - pathMatchType: targets.pathMatchType + pathMatchType: targets.pathMatchType, + rewritePath: targets.rewritePath, + rewritePathType: targets.rewritePathType }) .from(targets) .leftJoin(sites, eq(sites.siteId, targets.siteId)) diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 928a1a55..1e47ce96 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -28,7 +28,9 @@ const updateTargetBodySchema = z port: z.number().int().min(1).max(65535).optional(), enabled: z.boolean().optional(), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable() + pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }) .strict() .refine((data) => Object.keys(data).length > 0, { diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 4bbb13d3..b713f98a 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -90,6 +90,217 @@ export async function traefikConfigProvider( } } + +function validatePathRewriteConfig( + path: string | null, + pathMatchType: string | null, + rewritePath: string | null, + rewritePathType: string | null +): { isValid: boolean; error?: string } { + // If no path matching is configured, no rewriting is possible + if (!path || !pathMatchType) { + if (rewritePath || rewritePathType) { + return { + isValid: false, + error: "Path rewriting requires path matching to be configured" + }; + } + return { isValid: true }; + } + + if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { + return { + isValid: false, + error: "Both rewritePath and rewritePathType must be specified together" + }; + } + + if (!rewritePath || !rewritePathType) { + return { isValid: true }; + } + + const validPathMatchTypes = ["exact", "prefix", "regex"]; + if (!validPathMatchTypes.includes(pathMatchType)) { + return { + isValid: false, + error: `Invalid pathMatchType: ${pathMatchType}. Must be one of: ${validPathMatchTypes.join(", ")}` + }; + } + + const validRewritePathTypes = ["exact", "prefix", "regex", "stripPrefix"]; + if (!validRewritePathTypes.includes(rewritePathType)) { + return { + isValid: false, + error: `Invalid rewritePathType: ${rewritePathType}. Must be one of: ${validRewritePathTypes.join(", ")}` + }; + } + + if (pathMatchType === "regex") { + try { + new RegExp(path); + } catch (e) { + return { + isValid: false, + error: `Invalid regex pattern in path: ${path}` + }; + } + } + + if (rewritePathType === "regex") { + // For regex rewrite type, we don't validate the replacement pattern + // as it may contain capture groups like $1, $2, etc. + // The regex engine will handle validation at runtime + } + + // Validate path formats for non-regex types + if (pathMatchType !== "regex" && !path.startsWith("/")) { + return { + isValid: false, + error: "Path must start with '/' for exact and prefix matching" + }; + } + + // Additional validation for stripPrefix + if (rewritePathType === "stripPrefix") { + if (pathMatchType !== "prefix") { + logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); + } + // For stripPrefix, rewritePath is optional (can be empty to just strip) + if (rewritePath && !rewritePath.startsWith("/") && rewritePath !== "") { + return { + isValid: false, + error: "stripPrefix rewritePath must start with '/' or be empty" + }; + } + } + + return { isValid: true }; +} + + +function createPathRewriteMiddleware( + middlewareName: string, + path: string, + pathMatchType: string, + rewritePath: string, + rewritePathType: string +): { [key: string]: any } { + const middlewares: { [key: string]: any } = {}; + + switch (rewritePathType) { + case "exact": + // Replace the entire path with the exact rewrite path + middlewares[middlewareName] = { + replacePathRegex: { + regex: "^.*$", + replacement: rewritePath + } + }; + break; + + case "prefix": + // Replace matched prefix with new prefix, preserve the rest + switch (pathMatchType) { + case "prefix": + middlewares[middlewareName] = { + replacePathRegex: { + regex: `^${escapeRegex(path)}(.*)`, + replacement: `${rewritePath}$1` + } + }; + break; + case "exact": + middlewares[middlewareName] = { + replacePathRegex: { + regex: `^${escapeRegex(path)}$`, + replacement: rewritePath + } + }; + break; + case "regex": + // For regex path matching with prefix rewrite, we assume the regex has capture groups + middlewares[middlewareName] = { + replacePathRegex: { + regex: path, + replacement: rewritePath + } + }; + break; + } + break; + + case "regex": + // Use advanced regex replacement - works with any match type + let regexPattern: string; + if (pathMatchType === "regex") { + regexPattern = path; + } else if (pathMatchType === "prefix") { + regexPattern = `^${escapeRegex(path)}(.*)`; + } else { // exact + regexPattern = `^${escapeRegex(path)}$`; + } + + middlewares[middlewareName] = { + replacePathRegex: { + regex: regexPattern, + replacement: rewritePath + } + }; + break; + + case "stripPrefix": + // Strip the matched prefix and optionally add new path + if (pathMatchType === "prefix") { + middlewares[middlewareName] = { + stripPrefix: { + prefixes: [path] + } + }; + + // If rewritePath is provided and not empty, add it as a prefix after stripping + if (rewritePath && rewritePath !== "" && rewritePath !== "/") { + const addPrefixMiddlewareName = `${middlewareName.replace('-rewrite', '')}-add-prefix-middleware`; + middlewares[addPrefixMiddlewareName] = { + addPrefix: { + prefix: rewritePath + } + }; + // Return both middlewares with a special flag to indicate chaining + return { + middlewares, + chain: [middlewareName, addPrefixMiddlewareName] + }; + } + } else { + // For exact and regex matches, use replacePathRegex to strip + let regexPattern: string; + if (pathMatchType === "exact") { + regexPattern = `^${escapeRegex(path)}$`; + } else if (pathMatchType === "regex") { + regexPattern = path; + } else { + // This shouldn't happen due to earlier validation, but handle gracefully + regexPattern = `^${escapeRegex(path)}`; + } + + const replacement = rewritePath || "/"; + middlewares[middlewareName] = { + replacePathRegex: { + regex: regexPattern, + replacement: replacement + } + }; + } + break; + + default: + logger.error(`Unknown rewritePathType: ${rewritePathType}`); + throw new Error(`Unknown rewritePathType: ${rewritePathType}`); + } + + return { middlewares }; +} + export async function getTraefikConfig( exitNodeId: number, siteTypes: string[] @@ -133,7 +344,8 @@ export async function getTraefikConfig( internalPort: targets.internalPort, path: targets.path, pathMatchType: targets.pathMatchType, - + rewritePath: targets.rewritePath, + rewritePathType: targets.rewritePathType, // Site fields siteId: sites.siteId, siteType: sites.type, @@ -163,12 +375,28 @@ export async function getTraefikConfig( const resourceId = row.resourceId; const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; + const rewritePath = row.rewritePath || ""; + const rewritePathType = row.rewritePathType || ""; - // Create a unique key combining resourceId and path+pathMatchType - const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-"); + // Create a unique key combining resourceId, path config, and rewrite config + const pathKey = [targetPath, pathMatchType, rewritePath, rewritePathType] + .filter(Boolean) + .join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); if (!resourcesMap.has(mapKey)) { + const validation = validatePathRewriteConfig( + row.path, + row.pathMatchType, + row.rewritePath, + row.rewritePathType + ); + + if (!validation.isValid) { + logger.error(`Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}`); + return; + } + resourcesMap.set(mapKey, { resourceId: row.resourceId, fullDomain: row.fullDomain, @@ -186,7 +414,9 @@ export async function getTraefikConfig( targets: [], headers: row.headers, path: row.path, // the targets will all have the same path - pathMatchType: row.pathMatchType // the targets will all have the same pathMatchType + pathMatchType: row.pathMatchType, // the targets will all have the same pathMatchType + rewritePath: row.rewritePath, + rewritePathType: row.rewritePathType }); } @@ -199,6 +429,8 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, + rewritePath: row.rewritePath, + rewritePathType: row.rewritePathType, site: { siteId: row.siteId, type: row.siteType, @@ -241,19 +473,14 @@ export async function getTraefikConfig( } if (resource.http) { - if (!resource.domainId) { + if (!resource.domainId || !resource.fullDomain) { continue; } - if (!resource.fullDomain) { - continue; - } - - // add routers and services empty objects if they don't exist + // Initialize routers and services if they don't exist if (!config_output.http.routers) { config_output.http.routers = {}; } - if (!config_output.http.services) { config_output.http.services = {}; } @@ -288,24 +515,66 @@ export async function getTraefikConfig( certResolver: certResolver, ...(preferWildcardCert ? { - domains: [ - { - main: wildCard - } - ] - } + domains: [ + { + main: wildCard + } + ] + } : {}) }; } - const additionalMiddlewares = - config.getRawConfig().traefik.additional_middlewares || []; + const additionalMiddlewares = + config.getRawConfig().traefik.additional_middlewares || []; const routerMiddlewares = [ badgerMiddlewareName, ...additionalMiddlewares ]; + // Handle path rewriting middleware + if (resource.rewritePath && + resource.path && + resource.pathMatchType && + resource.rewritePathType) { + + const rewriteMiddlewareName = `${resource.id}-${key}-rewrite`; + + try { + const rewriteResult = createPathRewriteMiddleware( + rewriteMiddlewareName, + resource.path, + resource.pathMatchType, + resource.rewritePath, + resource.rewritePathType + ); + + // Initialize middlewares object if it doesn't exist + if (!config_output.http.middlewares) { + config_output.http.middlewares = {}; + } + + // Add the middleware(s) to the config + Object.assign(config_output.http.middlewares, rewriteResult.middlewares); + + // Add middleware(s) to the router middleware chain + if (rewriteResult.chain) { + // For chained middlewares (like stripPrefix + addPrefix) + routerMiddlewares.push(...rewriteResult.chain); + } else { + // Single middleware + routerMiddlewares.push(rewriteMiddlewareName); + } + + logger.info(`Created path rewrite middleware for ${key}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); + } catch (error) { + logger.error(`Failed to create path rewrite middleware for ${key}: ${error}`); + // Continue without the rewrite middleware rather than failing completely + } + } + + // Handle custom headers middleware if (resource.headers || resource.setHostHeader) { // if there are headers, parse them into an object const headersObj: { [key: string]: string } = {}; @@ -406,15 +675,15 @@ export async function getTraefikConfig( return ( (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { - if (!target.enabled) { - return false; - } + .filter((target: TargetWithSite) => { + if (!target.enabled) { + return false; + } // If any sites are online, exclude offline sites - if (anySitesOnline && !target.site.online) { - return false; - } + if (anySitesOnline && !target.site.online) { + return false; + } if ( target.site.type === "local" || @@ -427,33 +696,33 @@ export async function getTraefikConfig( ) { return false; } - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { if ( !target.internalPort || !target.method || !target.site.subnet ) { - return false; + return false; } } return true; - }) - .map((target: TargetWithSite) => { + }) + .map((target: TargetWithSite) => { if ( target.site.type === "local" || target.site.type === "wireguard" ) { - return { - url: `${target.method}://${target.ip}:${target.port}` - }; - } else if (target.site.type === "newt") { + return { + url: `${target.method}://${target.ip}:${target.port}` + }; + } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; - return { - url: `${target.method}://${ip}:${target.internalPort}` - }; - } - }) + return { + url: `${target.method}://${ip}:${target.internalPort}` + }; + } + }) // filter out duplicates .filter( (v, i, a) => @@ -465,14 +734,14 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - cookie: { + sticky: { + cookie: { name: "p_sticky", // TODO: make this configurable via config.yml like other cookies - secure: resource.ssl, - httpOnly: true - } - } - } + secure: resource.ssl, + httpOnly: true + } + } + } : {}) } }; @@ -549,7 +818,7 @@ export async function getTraefikConfig( !target.internalPort || !target.site.subnet ) { - return false; + return false; } } return true; @@ -573,13 +842,13 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - ipStrategy: { - depth: 0, - sourcePort: true - } - } - } + sticky: { + ipStrategy: { + depth: 0, + sourcePort: true + } + } + } : {}) } }; @@ -590,10 +859,19 @@ export async function getTraefikConfig( function sanitizePath(path: string | null | undefined): string | undefined { if (!path) return undefined; - // clean any non alphanumeric characters from the path and replace with dashes - // the path cant be too long either, so limit to 50 characters + + // For path rewriting, we need to be more careful about sanitization + // Only limit length and ensure it's a valid path structure if (path.length > 50) { path = path.substring(0, 50); + logger.warn(`Path truncated to 50 characters: ${path}`); } - return path.replace(/[^a-zA-Z0-9]/g, ""); + + // Don't remove special characters as they might be part of regex patterns + // Just ensure it's not empty after trimming + return path.trim() || undefined; } + +function escapeRegex(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} \ No newline at end of file diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 1232e542..23a55494 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -105,7 +105,9 @@ const addTargetSchema = z.object({ port: z.coerce.number().int().positive(), siteId: z.number().int().positive(), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable() + pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }).refine( (data) => { // If path is provided, pathMatchType must be provided @@ -138,7 +140,23 @@ const addTargetSchema = z.object({ { message: "Invalid path configuration" } -); +) + .refine( + (data) => { + // If rewritePath is provided, rewritePathType must be provided + if (data.rewritePath && !data.rewritePathType) { + return false; + } + // If rewritePathType is provided, rewritePath must be provided + if (data.rewritePathType && !data.rewritePath) { + return false; + } + return true; + }, + { + message: "Invalid rewrite path configuration" + } + ); const targetsSettingsSchema = z.object({ stickySession: z.boolean() @@ -259,8 +277,10 @@ export default function ReverseProxyTargets(props: { method: resource.http ? "http" : null, port: "" as any as number, path: null, - pathMatchType: null - } + pathMatchType: null, + rewritePath: null, + rewritePathType: null, + } as z.infer }); const watchedIp = addTargetForm.watch("ip"); @@ -436,6 +456,8 @@ export default function ReverseProxyTargets(props: { ...data, path: data.path || null, pathMatchType: data.pathMatchType || null, + rewritePath: data.rewritePath || null, + rewritePathType: data.rewritePathType || null, siteType: site?.type || null, enabled: true, targetId: new Date().getTime(), @@ -449,7 +471,9 @@ export default function ReverseProxyTargets(props: { method: resource.http ? "http" : null, port: "" as any as number, path: null, - pathMatchType: null + pathMatchType: null, + rewritePath: null, + rewritePathType: null, }); } @@ -469,11 +493,11 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...data, - updated: true, - siteType: site?.type || null - } + ...target, + ...data, + updated: true, + siteType: site?.type || null + } : target ) ); @@ -494,7 +518,9 @@ export default function ReverseProxyTargets(props: { enabled: target.enabled, siteId: target.siteId, path: target.path, - pathMatchType: target.pathMatchType + pathMatchType: target.pathMatchType, + rewritePath: target.rewritePath, + rewritePathType: target.rewritePathType }; if (target.new) { @@ -590,7 +616,7 @@ export default function ReverseProxyTargets(props: { } }} > - + {t("matchPath")} + + {t("matchPath")} ); } @@ -693,7 +719,7 @@ export default function ReverseProxyTargets(props: { className={cn( "justify-between flex-1", !row.original.siteId && - "text-muted-foreground" + "text-muted-foreground" )} > {row.original.siteId @@ -772,31 +798,31 @@ export default function ReverseProxyTargets(props: { }, ...(resource.http ? [ - { - accessorKey: "method", - header: t("method"), - cell: ({ row }: { row: Row }) => ( - - ) - } - ] + { + accessorKey: "method", + header: t("method"), + cell: ({ row }: { row: Row }) => ( + + ) + } + ] : []), { accessorKey: "ip", @@ -856,6 +882,102 @@ export default function ReverseProxyTargets(props: { /> ) }, + { + accessorKey: "rewritePath", + header: t("rewritePath"), + cell: ({ row }) => { + const [showRewritePathInput, setShowRewritePathInput] = useState( + !!(row.original.rewritePath || row.original.rewritePathType) + ); + + if (!showRewritePathInput) { + const noPathMatch = + !row.original.path && !row.original.pathMatchType; + return ( + + ); + } + + return ( +
+ + + + + { + const value = e.target.value.trim(); + if (!value) { + setShowRewritePathInput(false); + updateTarget(row.original.targetId, { + ...row.original, + rewritePath: null, + rewritePathType: null + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + rewritePath: value + }); + } + }} + /> +
+ ); + } + }, // { // accessorKey: "protocol", // header: t('targetProtocol'), @@ -968,21 +1090,21 @@ export default function ReverseProxyTargets(props: { className={cn( "justify-between flex-1", !field.value && - "text-muted-foreground" + "text-muted-foreground" )} > {field.value ? sites.find( - ( - site - ) => - site.siteId === - field.value - ) - ?.name + ( + site + ) => + site.siteId === + field.value + ) + ?.name : t( - "siteSelect" - )} + "siteSelect" + )} @@ -1048,34 +1170,34 @@ export default function ReverseProxyTargets(props: { ); return selectedSite && selectedSite.type === - "newt" + "newt" ? (() => { - const dockerState = - getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })() + const dockerState = + getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })() : null; })()}
@@ -1303,12 +1425,12 @@ export default function ReverseProxyTargets(props: { {header.isPlaceholder ? null : flexRender( - header - .column - .columnDef - .header, - header.getContext() - )} + header + .column + .columnDef + .header, + header.getContext() + )} ) )} From 90d3ac07a9f1c490746a8fe72a97b1de1ddbbee9 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 28 Sep 2025 11:45:49 +0530 Subject: [PATCH 076/322] add rewrite path to create resource page --- .../settings/resources/create/page.tsx | 134 +++++++++++++++++- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index f551e418..e8e7d68c 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -116,7 +116,9 @@ const addTargetSchema = z.object({ port: z.coerce.number().int().positive(), siteId: z.number().int().positive(), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable() + pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }).refine( (data) => { // If path is provided, pathMatchType must be provided @@ -149,7 +151,23 @@ const addTargetSchema = z.object({ { message: "Invalid path configuration" } -); +) + .refine( + (data) => { + // If rewritePath is provided, rewritePathType must be provided + if (data.rewritePath && !data.rewritePathType) { + return false; + } + // If rewritePathType is provided, rewritePath must be provided + if (data.rewritePathType && !data.rewritePath) { + return false; + } + return true; + }, + { + message: "Invalid rewrite path configuration" + } + ); type BaseResourceFormValues = z.infer; type HttpResourceFormValues = z.infer; @@ -240,8 +258,10 @@ export default function Page() { method: baseForm.watch("http") ? "http" : null, port: "" as any as number, path: null, - pathMatchType: null - } + pathMatchType: null, + rewritePath: null, + rewritePathType: null, + } as z.infer }); const watchedIp = addTargetForm.watch("ip"); @@ -313,6 +333,8 @@ export default function Page() { ...data, path: data.path || null, pathMatchType: data.pathMatchType || null, + rewritePath: data.rewritePath || null, + rewritePathType: data.rewritePathType || null, siteType: site?.type || null, enabled: true, targetId: new Date().getTime(), @@ -326,7 +348,9 @@ export default function Page() { method: baseForm.watch("http") ? "http" : null, port: "" as any as number, path: null, - pathMatchType: null + pathMatchType: null, + rewritePath: null, + rewritePathType: null, }); } @@ -422,7 +446,9 @@ export default function Page() { enabled: target.enabled, siteId: target.siteId, path: target.path, - pathMatchType: target.pathMatchType + pathMatchType: target.pathMatchType, + rewritePath: target.rewritePath, + rewritePathType: target.rewritePathType }; await api.put(`/resource/${id}/target`, data); @@ -820,6 +846,102 @@ export default function Page() { /> ) }, + { + accessorKey: "rewritePath", + header: t("rewritePath"), + cell: ({ row }) => { + const [showRewritePathInput, setShowRewritePathInput] = useState( + !!(row.original.rewritePath || row.original.rewritePathType) + ); + + if (!showRewritePathInput) { + const noPathMatch = + !row.original.path && !row.original.pathMatchType; + return ( + + ); + } + + return ( +
+ + + + + { + const value = e.target.value.trim(); + if (!value) { + setShowRewritePathInput(false); + updateTarget(row.original.targetId, { + ...row.original, + rewritePath: null, + rewritePathType: null + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + rewritePath: value + }); + } + }} + /> +
+ ); + } + }, { accessorKey: "enabled", header: t("enabled"), From 218a5ec9e4f5bf2802b0d0625d498582ec76ec24 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 28 Sep 2025 12:53:37 +0530 Subject: [PATCH 077/322] fix traefik config file --- server/routers/traefik/getTraefikConfig.ts | 57 ++++++++++------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index b713f98a..49bb50fd 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -146,12 +146,6 @@ function validatePathRewriteConfig( } } - if (rewritePathType === "regex") { - // For regex rewrite type, we don't validate the replacement pattern - // as it may contain capture groups like $1, $2, etc. - // The regex engine will handle validation at runtime - } - // Validate path formats for non-regex types if (pathMatchType !== "regex" && !path.startsWith("/")) { return { @@ -184,7 +178,7 @@ function createPathRewriteMiddleware( pathMatchType: string, rewritePath: string, rewritePathType: string -): { [key: string]: any } { +): { middlewares: { [key: string]: any }; chain?: string[] } { const middlewares: { [key: string]: any } = {}; switch (rewritePathType) { @@ -259,13 +253,12 @@ function createPathRewriteMiddleware( // If rewritePath is provided and not empty, add it as a prefix after stripping if (rewritePath && rewritePath !== "" && rewritePath !== "/") { - const addPrefixMiddlewareName = `${middlewareName.replace('-rewrite', '')}-add-prefix-middleware`; + const addPrefixMiddlewareName = `addprefix-${middlewareName.replace('rewrite-', '')}`; middlewares[addPrefixMiddlewareName] = { addPrefix: { prefix: rewritePath } }; - // Return both middlewares with a special flag to indicate chaining return { middlewares, chain: [middlewareName, addPrefixMiddlewareName] @@ -279,7 +272,6 @@ function createPathRewriteMiddleware( } else if (pathMatchType === "regex") { regexPattern = path; } else { - // This shouldn't happen due to earlier validation, but handle gracefully regexPattern = `^${escapeRegex(path)}`; } @@ -538,9 +530,10 @@ export async function getTraefikConfig( resource.path && resource.pathMatchType && resource.rewritePathType) { - - const rewriteMiddlewareName = `${resource.id}-${key}-rewrite`; - + + // Create a unique middleware name + const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${sanitizeForMiddlewareName(key)}`; + try { const rewriteResult = createPathRewriteMiddleware( rewriteMiddlewareName, @@ -567,17 +560,17 @@ export async function getTraefikConfig( routerMiddlewares.push(rewriteMiddlewareName); } - logger.info(`Created path rewrite middleware for ${key}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); + logger.info(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); } catch (error) { - logger.error(`Failed to create path rewrite middleware for ${key}: ${error}`); + logger.error(`Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}`); // Continue without the rewrite middleware rather than failing completely } } // Handle custom headers middleware if (resource.headers || resource.setHostHeader) { - // if there are headers, parse them into an object const headersObj: { [key: string]: string } = {}; + if (resource.headers) { let headersArr: { name: string; value: string }[] = []; try { @@ -586,9 +579,7 @@ export async function getTraefikConfig( value: string; }[]; } catch (e) { - logger.warn( - `Failed to parse headers for resource ${resource.resourceId}: ${e}` - ); + logger.warn(`Failed to parse headers for resource ${resource.resourceId}: ${e}`); } headersArr.forEach((header) => { @@ -600,9 +591,7 @@ export async function getTraefikConfig( headersObj["Host"] = resource.setHostHeader; } - // check if the object is not empty if (Object.keys(headersObj).length > 0) { - // Add the headers middleware if (!config_output.http.middlewares) { config_output.http.middlewares = {}; } @@ -616,8 +605,10 @@ export async function getTraefikConfig( } } + // Build routing rules let rule = `Host(\`${fullDomain}\`)`; let priority = 100; + if (resource.path && resource.pathMatchType) { priority += 1; // add path to rule based on match type @@ -763,7 +754,7 @@ export async function getTraefikConfig( } } else { // Non-HTTP (TCP/UDP) configuration - if (!resource.enableProxy) { + if (!resource.enableProxy || !resource.proxyPort) { continue; } @@ -860,16 +851,22 @@ export async function getTraefikConfig( function sanitizePath(path: string | null | undefined): string | undefined { if (!path) return undefined; - // For path rewriting, we need to be more careful about sanitization - // Only limit length and ensure it's a valid path structure - if (path.length > 50) { - path = path.substring(0, 50); - logger.warn(`Path truncated to 50 characters: ${path}`); + const trimmed = path.trim(); + if (!trimmed) return undefined; + + // Preserve path structure for rewriting, only warn if very long + if (trimmed.length > 1000) { + logger.warn(`Path exceeds 1000 characters: ${trimmed.substring(0, 100)}...`); + return trimmed.substring(0, 1000); } - // Don't remove special characters as they might be part of regex patterns - // Just ensure it's not empty after trimming - return path.trim() || undefined; + return trimmed; +} + +function sanitizeForMiddlewareName(str: string): string { + // Replace any characters that aren't alphanumeric or dash with dash + // and remove consecutive dashes + return str.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); } function escapeRegex(string: string): string { From 3722b6772433a9ff597f1fe2df3c060eb6d23efb Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 28 Sep 2025 13:05:58 +0530 Subject: [PATCH 078/322] preserves the rest of the path after the matched prefix --- server/routers/traefik/getTraefikConfig.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 49bb50fd..789b2f82 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -184,9 +184,10 @@ function createPathRewriteMiddleware( switch (rewritePathType) { case "exact": // Replace the entire path with the exact rewrite path + let exactPattern = `^${escapeRegex(path)}$`; middlewares[middlewareName] = { replacePathRegex: { - regex: "^.*$", + regex: exactPattern, replacement: rewritePath } }; From a97b6efe9cc2b6d6095b76929902932692574c8e Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 29 Sep 2025 16:40:37 +0530 Subject: [PATCH 079/322] redesign path match and rewrite modal --- .../resources/[niceId]/proxy/page.tsx | 243 ++++++--------- src/components/PathMatchRenameModal.tsx | 293 ++++++++++++++++++ 2 files changed, 385 insertions(+), 151 deletions(-) create mode 100644 src/components/PathMatchRenameModal.tsx diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 23a55494..4442e150 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -73,6 +73,7 @@ import { CircleCheck, CircleX, ArrowRight, + Plus, MoveRight } from "lucide-react"; import { ContainersSelector } from "@app/components/ContainersSelector"; @@ -95,9 +96,9 @@ import { CommandItem, CommandList } from "@app/components/ui/command"; -import { Badge } from "@app/components/ui/badge"; import { parseHostTarget } from "@app/lib/parseHostTarget"; import { HeadersInput } from "@app/components/HeadersInput"; +import { PathMatchDisplay, PathMatchModal, PathRewriteDisplay, PathRewriteModal } from "@app/components/PathMatchRenameModal"; const addTargetSchema = z.object({ ip: z.string().refine(isTargetValid), @@ -597,93 +598,64 @@ export default function ReverseProxyTargets(props: { accessorKey: "path", header: t("matchPath"), cell: ({ row }) => { - const [showPathInput, setShowPathInput] = useState( - !!(row.original.path || row.original.pathMatchType) - ); + const hasPathMatch = !!(row.original.path || row.original.pathMatchType); - if (!showPathInput) { - return ( - - ); - } - - return ( -
- - { - const value = e.target.value.trim(); - if (!value) { - setShowPathInput(false); - updateTarget(row.original.targetId, { - ...row.original, - path: null, - pathMatchType: null - }); - } else { - updateTarget(row.original.targetId, { - ...row.original, - path: value - }); - } - }} /> - + {/* */}
+ ) : ( + updateTarget(row.original.targetId, config)} + trigger={ + + } + /> ); - } + }, }, { accessorKey: "siteId", @@ -886,98 +858,68 @@ export default function ReverseProxyTargets(props: { accessorKey: "rewritePath", header: t("rewritePath"), cell: ({ row }) => { - const [showRewritePathInput, setShowRewritePathInput] = useState( - !!(row.original.rewritePath || row.original.rewritePathType) - ); + const hasRewritePath = !!(row.original.rewritePath || row.original.rewritePathType); + const noPathMatch = !row.original.path && !row.original.pathMatchType; - if (!showRewritePathInput) { - const noPathMatch = - !row.original.path && !row.original.pathMatchType; - return ( - - ); - } - - return ( -
+ onChange={(config) => updateTarget(row.original.targetId, config)} + trigger={ + + } + /> - - - - { - const value = e.target.value.trim(); - if (!value) { - setShowRewritePathInput(false); - updateTarget(row.original.targetId, { - ...row.original, - rewritePath: null, - rewritePathType: null - }); - } else { - updateTarget(row.original.targetId, { - ...row.original, - rewritePath: value - }); - } - }} - />
+ ) : ( + updateTarget(row.original.targetId, config)} + trigger={ + + } + disabled={noPathMatch} + /> ); - } + }, }, + // { // accessorKey: "protocol", // header: t('targetProtocol'), @@ -1649,7 +1591,6 @@ export default function ReverseProxyTargets(props: { } function isIPInSubnet(subnet: string, ip: string): boolean { - // Split subnet into IP and mask parts const [subnetIP, maskBits] = subnet.split("/"); const mask = parseInt(maskBits); diff --git a/src/components/PathMatchRenameModal.tsx b/src/components/PathMatchRenameModal.tsx new file mode 100644 index 00000000..574c9c70 --- /dev/null +++ b/src/components/PathMatchRenameModal.tsx @@ -0,0 +1,293 @@ +import { Pencil } from "lucide-react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@app/components/ui/dialog"; +import { Badge } from "@app/components/ui/badge"; +import { Label } from "@app/components/ui/label"; +import { useEffect, useState } from "react"; +import { Input } from "./ui/input"; +import { Button } from "./ui/button"; + + +export function PathMatchModal({ + value, + onChange, + trigger, +}: { + value: { path: string | null; pathMatchType: string | null }; + onChange: (config: { path: string | null; pathMatchType: string | null }) => void; + trigger: React.ReactNode; +}) { + const [open, setOpen] = useState(false); + const [matchType, setMatchType] = useState(value?.pathMatchType || "prefix"); + const [path, setPath] = useState(value?.path || ""); + + useEffect(() => { + if (open) { + setMatchType(value?.pathMatchType || "prefix"); + setPath(value?.path || ""); + } + }, [open, value]); + + const handleSave = () => { + onChange({ pathMatchType: matchType as any, path: path.trim() }); + setOpen(false); + }; + + const handleClear = () => { + onChange({ pathMatchType: null, path: null }); + setOpen(false); + }; + + const getPlaceholder = () => (matchType === "regex" ? "^/api/.*" : "/path"); + + const getHelpText = () => { + switch (matchType) { + case "prefix": + return "Example: /api matches /api, /api/users, etc."; + case "exact": + return "Example: /api matches only /api"; + case "regex": + return "Example: ^/api/.* matches /api/anything"; + default: + return ""; + } + }; + + return ( + + {trigger} + + + Configure Path Matching + + Set up how incoming requests should be matched based on their path. + + +
+
+ + +
+
+ + setPath(e.target.value)} + /> +

{getHelpText()}

+
+
+ + {value?.path && ( + + )} + + +
+
+ ); +} + + +export function PathRewriteModal({ + value, + onChange, + trigger, + disabled, +}: { + value: { rewritePath: string | null; rewritePathType: string | null }; + onChange: (config: { rewritePath: string | null; rewritePathType: string | null }) => void; + trigger: React.ReactNode; + disabled?: boolean; +}) { + const [open, setOpen] = useState(false); + const [rewriteType, setRewriteType] = useState(value?.rewritePathType || "prefix"); + const [rewritePath, setRewritePath] = useState(value?.rewritePath || ""); + + useEffect(() => { + if (open) { + setRewriteType(value?.rewritePathType || "prefix"); + setRewritePath(value?.rewritePath || ""); + } + }, [open, value]); + + const handleSave = () => { + onChange({ rewritePathType: rewriteType as any, rewritePath: rewritePath.trim() }); + setOpen(false); + }; + + const handleClear = () => { + onChange({ rewritePathType: null, rewritePath: null }); + setOpen(false); + }; + + const getPlaceholder = () => { + switch (rewriteType) { + case "regex": + return "/new/$1"; + case "stripPrefix": + return ""; + default: + return "/new-path"; + } + }; + + const getHelpText = () => { + switch (rewriteType) { + case "prefix": + return "Replace the matched prefix with this value"; + case "exact": + return "Replace the entire path with this value"; + case "regex": + return "Use capture groups like $1, $2 for replacement"; + case "stripPrefix": + return "Leave empty to strip prefix or provide new prefix"; + default: + return ""; + } + }; + + return ( + + + {trigger} + + + + Configure Path Rewriting + + Transform the matched path before forwarding to the target. + + +
+
+ + +
+
+ + setRewritePath(e.target.value)} + /> +

{getHelpText()}

+
+
+ + {value?.rewritePath && ( + + )} + + +
+
+ ); +} + +export function PathMatchDisplay({ + value, +}: { + value: { path: string | null; pathMatchType: string | null }; +}) { + if (!value?.path) return null; + + const getTypeLabel = (type: string | null) => { + const labels: Record = { + prefix: "Prefix", + exact: "Exact", + regex: "Regex", + }; + return labels[type || ""] || type; + }; + + return ( +
+ + {getTypeLabel(value.pathMatchType)} + + + {value.path} + + +
+ ); +} + + +export function PathRewriteDisplay({ + value, +}: { + value: { rewritePath: string | null; rewritePathType: string | null }; +}) { + if (!value?.rewritePath && value?.rewritePathType !== "stripPrefix") return null; + + const getTypeLabel = (type: string | null) => { + const labels: Record = { + prefix: "Prefix", + exact: "Exact", + regex: "Regex", + stripPrefix: "Strip", + }; + return labels[type || ""] || type; + }; + + return ( +
+ + {getTypeLabel(value.rewritePathType)} + + + {value.rewritePath || (strip)} + + +
+ ); +} From 7b2f1dd4c654b9fed12bbcdbfc33398c319989ed Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 29 Sep 2025 16:49:26 +0530 Subject: [PATCH 080/322] button fix --- src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 4442e150..4f81ed58 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -648,7 +648,7 @@ export default function ReverseProxyTargets(props: { }} onChange={(config) => updateTarget(row.original.targetId, config)} trigger={ - @@ -909,7 +909,7 @@ export default function ReverseProxyTargets(props: { }} onChange={(config) => updateTarget(row.original.targetId, config)} trigger={ - From 1b34ee7369545ccd58f2f54ada298fb512e182c5 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Tue, 30 Sep 2025 00:19:59 +0530 Subject: [PATCH 081/322] match and rewrite path ui improve for create resource --- server/routers/target/createTarget.ts | 2 +- .../settings/resources/create/page.tsx | 243 +++++++----------- 2 files changed, 93 insertions(+), 152 deletions(-) diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 6ed9d9aa..dd85c888 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -34,7 +34,7 @@ const createTargetSchema = z path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() // NEW: rewrite path type + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }) .strict(); diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index e8e7d68c..efb136cb 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -58,7 +58,7 @@ import { } from "@app/components/ui/popover"; import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"; import { cn } from "@app/lib/cn"; -import { ArrowRight, MoveRight, SquareArrowOutUpRight } from "lucide-react"; +import { ArrowRight, MoveRight, Plus, SquareArrowOutUpRight } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import Link from "next/link"; import { useTranslations } from "next-intl"; @@ -92,6 +92,7 @@ import { parseHostTarget } from "@app/lib/parseHostTarget"; import { toASCII, toUnicode } from 'punycode'; import { DomainRow } from "../../../../../components/DomainsTable"; import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; +import { PathMatchDisplay, PathMatchModal, PathRewriteDisplay, PathRewriteModal } from "@app/components/PathMatchRenameModal"; const baseResourceFormSchema = z.object({ @@ -152,7 +153,7 @@ const addTargetSchema = z.object({ message: "Invalid path configuration" } ) - .refine( + .refine( (data) => { // If rewritePath is provided, rewritePathType must be provided if (data.rewritePath && !data.rewritePathType) { @@ -575,93 +576,64 @@ export default function Page() { accessorKey: "path", header: t("matchPath"), cell: ({ row }) => { - const [showPathInput, setShowPathInput] = useState( - !!(row.original.path || row.original.pathMatchType) - ); + const hasPathMatch = !!(row.original.path || row.original.pathMatchType); - if (!showPathInput) { - return ( - - ); - } - - return ( -
- - { - const value = e.target.value.trim(); - if (!value) { - setShowPathInput(false); - updateTarget(row.original.targetId, { - ...row.original, - path: null, - pathMatchType: null - }); - } else { - updateTarget(row.original.targetId, { - ...row.original, - path: value - }); - } - }} /> - + {/* */}
+ ) : ( + updateTarget(row.original.targetId, config)} + trigger={ + + } + /> ); - } + }, }, { accessorKey: "siteId", @@ -850,97 +822,66 @@ export default function Page() { accessorKey: "rewritePath", header: t("rewritePath"), cell: ({ row }) => { - const [showRewritePathInput, setShowRewritePathInput] = useState( - !!(row.original.rewritePath || row.original.rewritePathType) - ); + const hasRewritePath = !!(row.original.rewritePath || row.original.rewritePathType); + const noPathMatch = !row.original.path && !row.original.pathMatchType; - if (!showRewritePathInput) { - const noPathMatch = - !row.original.path && !row.original.pathMatchType; - return ( - - ); - } - - return ( -
+ onChange={(config) => updateTarget(row.original.targetId, config)} + trigger={ + + } + /> - - - - { - const value = e.target.value.trim(); - if (!value) { - setShowRewritePathInput(false); - updateTarget(row.original.targetId, { - ...row.original, - rewritePath: null, - rewritePathType: null - }); - } else { - updateTarget(row.original.targetId, { - ...row.original, - rewritePath: value - }); - } - }} - />
+ ) : ( + updateTarget(row.original.targetId, config)} + trigger={ + + } + disabled={noPathMatch} + /> ); - } + }, }, { accessorKey: "enabled", From 574cd2a754d524bf1023e051ab380093565870b7 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Tue, 30 Sep 2025 16:56:24 +0530 Subject: [PATCH 082/322] make rewrite data null if no match added --- server/routers/traefik/getTraefikConfig.ts | 7 +++---- src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx | 2 ++ src/app/[orgId]/settings/resources/create/page.tsx | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 789b2f82..7acdcabc 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -183,7 +183,7 @@ function createPathRewriteMiddleware( switch (rewritePathType) { case "exact": - // Replace the entire path with the exact rewrite path + // Replace the path with the exact rewrite path let exactPattern = `^${escapeRegex(path)}$`; middlewares[middlewareName] = { replacePathRegex: { @@ -549,10 +549,10 @@ export async function getTraefikConfig( config_output.http.middlewares = {}; } - // Add the middleware(s) to the config + // the middleware to the config Object.assign(config_output.http.middlewares, rewriteResult.middlewares); - // Add middleware(s) to the router middleware chain + // middlewares to the router middleware chain if (rewriteResult.chain) { // For chained middlewares (like stripPrefix + addPrefix) routerMiddlewares.push(...rewriteResult.chain); @@ -564,7 +564,6 @@ export async function getTraefikConfig( logger.info(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); } catch (error) { logger.error(`Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}`); - // Continue without the rewrite middleware rather than failing completely } } diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 4f81ed58..b37cfba9 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -632,6 +632,8 @@ export default function ReverseProxyTargets(props: { ...row.original, path: null, pathMatchType: null, + rewritePath: null, + rewritePathType: null }); }} > diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index efb136cb..bb19cc79 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -610,6 +610,8 @@ export default function Page() { ...row.original, path: null, pathMatchType: null, + rewritePath: null, + rewritePathType: null }); }} > From 664aa6ed2a83f174a337f30cc1544d381614b076 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Tue, 30 Sep 2025 23:20:42 +0530 Subject: [PATCH 083/322] update blueprints --- server/lib/blueprints/proxyResources.ts | 8 ++++++-- server/lib/blueprints/types.ts | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index c6ab6f40..9b349281 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -107,7 +107,9 @@ export async function updateProxyResources( enabled: targetData.enabled, internalPort: internalPortToCreate, path: targetData.path, - pathMatchType: targetData["path-match"] + pathMatchType: targetData["path-match"], + rewritePath: targetData.rewritePath, + rewritePathType: targetData["rewrite-match"] }) .returning(); @@ -327,7 +329,9 @@ export async function updateProxyResources( port: targetData.port, enabled: targetData.enabled, path: targetData.path, - pathMatchType: targetData["path-match"] + pathMatchType: targetData["path-match"], + rewritePath: targetData.rewritePath, + rewritePathType: targetData["rewrite-match"] }) .where(eq(targets.targetId, existingTarget.targetId)) .returning(); diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index 9b3a7a20..dda672a6 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -14,7 +14,9 @@ export const TargetSchema = z.object({ enabled: z.boolean().optional().default(true), "internal-port": z.number().int().min(1).max(65535).optional(), path: z.string().optional(), - "path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable() + "path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional(), + "rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }); export type TargetData = z.infer; @@ -183,7 +185,7 @@ export const ClientResourceSchema = z.object({ "proxy-port": z.number().min(1).max(65535), "hostname": z.string().min(1).max(255), "internal-port": z.number().min(1).max(65535), - enabled: z.boolean().optional().default(true) + enabled: z.boolean().optional().default(true) }); // Schema for the entire configuration object From 51fad19d0ded1561c338cc1f08b5d53743157249 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 30 Sep 2025 14:32:48 -0700 Subject: [PATCH 084/322] Sanitize all keys --- server/routers/traefik/getTraefikConfig.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 7acdcabc..bf572f68 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -455,11 +455,13 @@ export async function getTraefikConfig( for (const [key, resource] of resourcesMap.entries()) { const targets = resource.targets; - const routerName = `${key}-router`; - const serviceName = `${key}-service`; + const sanatizedKey = sanitizeForMiddlewareName(key); + + const routerName = `${sanatizedKey}-router`; + const serviceName = `${sanatizedKey}-service`; const fullDomain = `${resource.fullDomain}`; - const transportName = `${key}-transport`; - const headersMiddlewareName = `${key}-headers-middleware`; + const transportName = `${sanatizedKey}-transport`; + const headersMiddlewareName = `${sanatizedKey}-headers-middleware`; if (!resource.enabled) { continue; From 4cbf3fffb17fd20e3a2795bb604cd00d68da67d1 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 30 Sep 2025 14:47:06 -0700 Subject: [PATCH 085/322] Quiet up logs --- server/routers/traefik/getTraefikConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index bf572f68..495cdaef 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -563,7 +563,7 @@ export async function getTraefikConfig( routerMiddlewares.push(rewriteMiddlewareName); } - logger.info(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); + logger.debug(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); } catch (error) { logger.error(`Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}`); } From 8767d20c47d907b15ce79f5924f81c3a89c73da0 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Wed, 1 Oct 2025 13:03:26 +0530 Subject: [PATCH 086/322] add missing path / validation --- server/routers/traefik/getTraefikConfig.ts | 106 ++++++++++----------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 495cdaef..fa722ed5 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -108,13 +108,13 @@ function validatePathRewriteConfig( return { isValid: true }; } - if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { - return { - isValid: false, - error: "Both rewritePath and rewritePathType must be specified together" - }; + if (rewritePathType !== "stripPrefix") { + if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { + return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; + } } + if (!rewritePath || !rewritePathType) { return { isValid: true }; } @@ -146,26 +146,12 @@ function validatePathRewriteConfig( } } - // Validate path formats for non-regex types - if (pathMatchType !== "regex" && !path.startsWith("/")) { - return { - isValid: false, - error: "Path must start with '/' for exact and prefix matching" - }; - } // Additional validation for stripPrefix if (rewritePathType === "stripPrefix") { if (pathMatchType !== "prefix") { logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); } - // For stripPrefix, rewritePath is optional (can be empty to just strip) - if (rewritePath && !rewritePath.startsWith("/") && rewritePath !== "") { - return { - isValid: false, - error: "stripPrefix rewritePath must start with '/' or be empty" - }; - } } return { isValid: true }; @@ -181,6 +167,14 @@ function createPathRewriteMiddleware( ): { middlewares: { [key: string]: any }; chain?: string[] } { const middlewares: { [key: string]: any } = {}; + if (pathMatchType !== "regex" && !path.startsWith("/")) { + path = `/${path}`; + } + + if (rewritePathType !== "regex" && rewritePath !== "" && !rewritePath.startsWith("/")) { + rewritePath = `/${rewritePath}`; + } + switch (rewritePathType) { case "exact": // Replace the path with the exact rewrite path @@ -260,9 +254,9 @@ function createPathRewriteMiddleware( prefix: rewritePath } }; - return { - middlewares, - chain: [middlewareName, addPrefixMiddlewareName] + return { + middlewares, + chain: [middlewareName, addPrefixMiddlewareName] }; } } else { @@ -387,7 +381,7 @@ export async function getTraefikConfig( if (!validation.isValid) { logger.error(`Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}`); - return; + return; } resourcesMap.set(mapKey, { @@ -520,8 +514,8 @@ export async function getTraefikConfig( }; } - const additionalMiddlewares = - config.getRawConfig().traefik.additional_middlewares || []; + const additionalMiddlewares = + config.getRawConfig().traefik.additional_middlewares || []; const routerMiddlewares = [ badgerMiddlewareName, @@ -529,14 +523,14 @@ export async function getTraefikConfig( ]; // Handle path rewriting middleware - if (resource.rewritePath && - resource.path && - resource.pathMatchType && + if (resource.rewritePath && + resource.path && + resource.pathMatchType && resource.rewritePathType) { - + // Create a unique middleware name const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${sanitizeForMiddlewareName(key)}`; - + try { const rewriteResult = createPathRewriteMiddleware( rewriteMiddlewareName, @@ -572,7 +566,7 @@ export async function getTraefikConfig( // Handle custom headers middleware if (resource.headers || resource.setHostHeader) { const headersObj: { [key: string]: string } = {}; - + if (resource.headers) { let headersArr: { name: string; value: string }[] = []; try { @@ -668,15 +662,15 @@ export async function getTraefikConfig( return ( (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { - if (!target.enabled) { - return false; - } + .filter((target: TargetWithSite) => { + if (!target.enabled) { + return false; + } // If any sites are online, exclude offline sites - if (anySitesOnline && !target.site.online) { - return false; - } + if (anySitesOnline && !target.site.online) { + return false; + } if ( target.site.type === "local" || @@ -689,33 +683,33 @@ export async function getTraefikConfig( ) { return false; } - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { if ( !target.internalPort || !target.method || !target.site.subnet ) { - return false; + return false; } } return true; - }) - .map((target: TargetWithSite) => { + }) + .map((target: TargetWithSite) => { if ( target.site.type === "local" || target.site.type === "wireguard" ) { - return { - url: `${target.method}://${target.ip}:${target.port}` - }; - } else if (target.site.type === "newt") { + return { + url: `${target.method}://${target.ip}:${target.port}` + }; + } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; - return { - url: `${target.method}://${ip}:${target.internalPort}` - }; - } - }) + return { + url: `${target.method}://${ip}:${target.internalPort}` + }; + } + }) // filter out duplicates .filter( (v, i, a) => @@ -729,7 +723,7 @@ export async function getTraefikConfig( ? { sticky: { cookie: { - name: "p_sticky", // TODO: make this configurable via config.yml like other cookies + name: "p_sticky", // TODO: make this configurable via config.yml like other cookies secure: resource.ssl, httpOnly: true } @@ -811,7 +805,7 @@ export async function getTraefikConfig( !target.internalPort || !target.site.subnet ) { - return false; + return false; } } return true; @@ -852,16 +846,16 @@ export async function getTraefikConfig( function sanitizePath(path: string | null | undefined): string | undefined { if (!path) return undefined; - + const trimmed = path.trim(); if (!trimmed) return undefined; - + // Preserve path structure for rewriting, only warn if very long if (trimmed.length > 1000) { logger.warn(`Path exceeds 1000 characters: ${trimmed.substring(0, 100)}...`); return trimmed.substring(0, 1000); } - + return trimmed; } From a5a7ca5fcc79504fced0ea02acd04c8adde02fb7 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:35 -0700 Subject: [PATCH 087/322] New translations en-us.json (French) --- messages/fr-FR.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 2b431ccf..f58db945 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Fournisseur Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "En-têtes personnalisés", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.", "domainPickerProvidedDomain": "Domaine fourni", "domainPickerFreeProvidedDomain": "Domaine fourni gratuitement", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici." -} +} \ No newline at end of file From 0d8c06595e2a49944c854db00d1bb746d6a0b924 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:37 -0700 Subject: [PATCH 088/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 6814a6ba..27f7a1a3 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC доставчик", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик", "customHeaders": "Персонализирани заглавия", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Заглавията трябва да бъдат във формат: Име на заглавието: стойност.", "domainPickerProvidedDomain": "Предоставен домейн", "domainPickerFreeProvidedDomain": "Безплатен предоставен домейн", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Редактиране на файл: docker-compose.yml", "emailVerificationRequired": "Потвърждението на Email е необходимо. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук." -} +} \ No newline at end of file From 672eec0c33f66f98763a57f289a99d9f60e202f5 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:38 -0700 Subject: [PATCH 089/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 424d0663..645c98be 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Vlastní záhlaví", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Záhlaví musí být ve formátu: název záhlaví: hodnota.", "domainPickerProvidedDomain": "Poskytnutá doména", "domainPickerFreeProvidedDomain": "Zdarma poskytnutá doména", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde." -} +} \ No newline at end of file From 9b4103be758bad5e88834845e55cc4373df06ab2 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:39 -0700 Subject: [PATCH 090/322] New translations en-us.json (German) --- messages/de-DE.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 98c0226c..e8e75ac0 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC Provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Eigene Kopfzeilen", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Header müssen im Format Header-Name: Wert sein.", "domainPickerProvidedDomain": "Angegebene Domain", "domainPickerFreeProvidedDomain": "Kostenlose Domain", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück." -} +} \ No newline at end of file From adf982fcd6f501ae3e5d932b75f8bbeba40fdfa8 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:41 -0700 Subject: [PATCH 091/322] New translations en-us.json (Italian) --- messages/it-IT.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index ec74a974..04ee775c 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Intestazioni Personalizzate", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.", "domainPickerProvidedDomain": "Dominio Fornito", "domainPickerFreeProvidedDomain": "Dominio Fornito Gratuito", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui." -} +} \ No newline at end of file From c50c2e2b016528ad2cc8fa478f8a85e06a4c9644 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:42 -0700 Subject: [PATCH 092/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 91fd5aa8..27457240 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC 공급자", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자", "customHeaders": "사용자 정의 헤더", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.", "domainPickerProvidedDomain": "제공된 도메인", "domainPickerFreeProvidedDomain": "무료 제공된 도메인", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요." -} +} \ No newline at end of file From bed45a5fbdc15597796a5230870c92972b183512 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:43 -0700 Subject: [PATCH 093/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index be4164ea..3c8de364 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -3,7 +3,7 @@ "setupNewOrg": "Nieuwe organisatie", "setupCreateOrg": "Nieuwe organisatie aanmaken", "setupCreateResources": "Bronnen aanmaken", - "setupOrgName": "Naam van de organisatie", + "setupOrgName": "Naam organisatie", "orgDisplayName": "Dit is de weergavenaam van uw organisatie.", "orgId": "Organisatie ID", "setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.", @@ -35,7 +35,7 @@ "createAccount": "Account Aanmaken", "viewSettings": "Instellingen weergeven", "delete": "Verwijderen", - "name": "Naam", + "name": "naam", "online": "Online", "offline": "Offline", "site": "Website", @@ -265,7 +265,7 @@ "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", "apiKeysList": "Uw API-sleutel", "apiKeysSave": "Uw API-sleutel opslaan", - "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "apiKeysInfo": "Uw API-sleutel is:", "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", "generate": "Genereren", @@ -994,7 +994,7 @@ "actionGetUser": "Gebruiker ophalen", "actionGetOrgUser": "Krijg organisatie-gebruiker", "actionListOrgDomains": "Lijst organisatie domeinen", - "actionCreateSite": "Site aanmaken", + "actionCreateSite": "Site maken", "actionDeleteSite": "Site verwijderen", "actionGetSite": "Site ophalen", "actionListSites": "Sites weergeven", @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Aangepaste headers", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", "domainPickerProvidedDomain": "Opgegeven domein", "domainPickerFreeProvidedDomain": "Gratis verstrekt domein", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug." -} +} \ No newline at end of file From 8e95f0b73fa824887fd409089eeb5583d0cc296f Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:44 -0700 Subject: [PATCH 094/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 7ff49860..8500739e 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Dostawca Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Niestandardowe nagłówki", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.", "domainPickerProvidedDomain": "Dostarczona domena", "domainPickerFreeProvidedDomain": "Darmowa oferowana domena", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj." -} +} \ No newline at end of file From 236e0f9ab67b787bc9f7cb84eeb66938833df116 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:46 -0700 Subject: [PATCH 095/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 275 ++++++++++++++++++++++---------------------- 1 file changed, 138 insertions(+), 137 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 04baacd9..0c39b7bc 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -8,25 +8,25 @@ "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", - "componentsErrorNoMemberCreate": "Não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", - "componentsErrorNoMember": "Não é atualmente um membro de nenhuma organização.", + "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", "welcome": "Bem-vindo ao Pangolin", "welcomeTo": "Bem-vindo ao", "componentsCreateOrg": "Criar uma organização", - "componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", + "componentsMember": "Você é membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", - "dismiss": "Rejeitar", + "dismiss": "Descartar", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", - "inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.", - "inviteErrorUser": "Lamentamos, mas parece que o convite que está a tentar aceder não é para este utilizador.", - "inviteLoginUser": "Verifique se você está logado como o utilizador correto.", - "inviteErrorNoUser": "Desculpe, mas parece que o convite que está a tentar aceder não é para um utilizador que existe.", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", + "inviteLoginUser": "Verifique se você está logado como o usuário correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", "inviteCreateUser": "Por favor, crie uma conta primeiro.", - "goHome": "Voltar ao inicio", - "inviteLogInOtherUser": "Fazer login como um utilizador diferente", + "goHome": "Ir para casa", + "inviteLogInOtherUser": "Fazer login como um usuário diferente", "createAnAccount": "Crie uma conta", - "inviteNotAccepted": "Convite não aceite", + "inviteNotAccepted": "Convite não aceito", "authCreateAccount": "Crie uma conta para começar", "authNoAccount": "Não possui uma conta?", "email": "e-mail", @@ -34,23 +34,23 @@ "confirmPassword": "Confirmar senha", "createAccount": "Criar conta", "viewSettings": "Visualizar configurações", - "delete": "apagar", + "delete": "excluir", "name": "Nome:", "online": "Disponível", "offline": "Desconectado", "site": "site", - "dataIn": "Dados de entrada", + "dataIn": "Dados em", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", - "siteConfirmDelete": "Confirmar que pretende apagar o site", + "siteConfirmDelete": "Confirmar exclusão do site", "siteDelete": "Excluir site", "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", - "siteManageSites": "Gerir sites", + "siteManageSites": "Gerenciar sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", @@ -79,10 +79,10 @@ "operatingSystem": "Sistema operacional", "commands": "Comandos", "recommended": "Recomendados", - "siteNewtDescription": "Para a melhor experiência do utilizador, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", - "siteErrorDelete": "Erro ao apagar site", + "siteErrorDelete": "Erro ao excluir site", "siteErrorUpdate": "Falha ao atualizar site", "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", "siteUpdated": "Site atualizado", @@ -105,12 +105,12 @@ "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", "siteInfo": "Informações do Site", "status": "SItuação", - "shareTitle": "Gerir links partilhados", + "shareTitle": "Gerenciar links de compartilhamento", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", "shareCreate": "Criar Link de Compartilhamento", - "shareErrorDelete": "Falha ao apagar o link", - "shareErrorDeleteMessage": "Ocorreu um erro ao apagar o link", + "shareErrorDelete": "Falha ao excluir o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", "shareDeleted": "Link excluído", "shareDeletedDescription": "O link foi eliminado", "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", @@ -127,13 +127,13 @@ "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", "shareErrorCreate": "Falha ao criar link de compartilhamento", "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", - "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", + "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", "shareTitleOptional": "Título (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", - "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", - "shareAccessHint": "Qualquer um com este link pode aceder o recurso. Compartilhe com cuidado.", + "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", "resourcesNotFound": "Nenhum recurso encontrado", @@ -145,11 +145,11 @@ "expires": "Expira", "never": "nunca", "shareErrorSelectResource": "Por favor, selecione um recurso", - "resourceTitle": "Gerir Recursos", + "resourceTitle": "Gerenciar Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", - "resourceErrorDelte": "Erro ao apagar recurso", + "resourceErrorDelte": "Erro ao excluir recurso", "authentication": "Autenticação", "protected": "Protegido", "notProtected": "Não Protegido", @@ -170,7 +170,7 @@ "siteNotFound": "Nenhum site encontrado.", "siteSelectionDescription": "Este site fornecerá conectividade ao destino.", "resourceType": "Tipo de Recurso", - "resourceTypeDescription": "Determine como você deseja aceder seu recurso", + "resourceTypeDescription": "Determine como você deseja acessar seu recurso", "resourceHTTPSSettings": "Configurações de HTTPS", "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", "domainType": "Tipo de domínio", @@ -192,7 +192,7 @@ "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", "resourceDelete": "Excluir Recurso", - "resourceDeleteConfirm": "Confirmar que pretende apagar o recurso", + "resourceDeleteConfirm": "Confirmar exclusão de recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -208,14 +208,14 @@ "passToAuth": "Passar para Autenticação", "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", - "orgGeneralSettingsDescription": "Gerir os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Guardar configurações gerais", - "saveSettings": "Guardar Configurações", + "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", + "saveGeneralSettings": "Salvar configurações gerais", + "saveSettings": "Salvar Configurações", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", - "orgDeleteConfirm": "Confirmar que pretende apagar a organização", - "orgMessageRemove": "Esta ação é irreversível e apagará todos os dados associados.", + "orgDeleteConfirm": "Confirmar exclusão da organização", + "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", "orgUpdated": "Organização atualizada", @@ -224,29 +224,29 @@ "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", "orgErrorFetch": "Falha ao buscar organizações", "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", - "orgErrorDelete": "Falha ao apagar organização", - "orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.", + "orgErrorDelete": "Falha ao excluir organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgMissing": "ID da Organização Ausente", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", - "accessUsersManage": "Gerir Utilizadores", - "accessUsersDescription": "Convidar utilizadores e adicioná-los a funções para gerir o acesso à sua organização", - "accessUsersSearch": "Procurar utilizadores...", + "accessUsersManage": "Gerenciar Usuários", + "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", + "accessUsersSearch": "Procurar usuários...", "accessUserCreate": "Criar Usuário", - "accessUserRemove": "Remover utilizador", + "accessUserRemove": "Remover usuário", "username": "Usuário:", "identityProvider": "Provedor de Identidade", "role": "Funções", "nameRequired": "O nome é obrigatório", - "accessRolesManage": "Gerir Funções", - "accessRolesDescription": "Configurar funções para gerir o acesso à sua organização", + "accessRolesManage": "Gerenciar Funções", + "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", "accessRolesSearch": "Pesquisar funções...", "accessRolesAdd": "Adicionar função", "accessRoleDelete": "Excluir Papel", "description": "Descrição:", "inviteTitle": "Convites Abertos", - "inviteDescription": "Gerir seus convites para outros utilizadores", + "inviteDescription": "Gerencie seus convites para outros usuários", "inviteSearch": "Procurar convites...", "minutes": "minutos", "hours": "horas", @@ -264,7 +264,7 @@ "apiKeysGeneralSettings": "Permissões", "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", "apiKeysList": "Sua Chave API", - "apiKeysSave": "Guardar Sua Chave API", + "apiKeysSave": "Salvar Sua Chave API", "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", "apiKeysInfo": "Sua chave API é:", "apiKeysConfirmCopy": "Eu copiei a chave API", @@ -277,33 +277,33 @@ "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", "apiKeysPermissionsGeneralSettings": "Permissões", "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", - "apiKeysPermissionsSave": "Guardar Permissões", + "apiKeysPermissionsSave": "Salvar Permissões", "apiKeysPermissionsTitle": "Permissões", "apiKeys": "Chaves API", "searchApiKeys": "Pesquisar chaves API...", "apiKeysAdd": "Gerar Chave API", - "apiKeysErrorDelete": "Erro ao apagar chave API", - "apiKeysErrorDeleteMessage": "Erro ao apagar chave API", + "apiKeysErrorDelete": "Erro ao excluir chave API", + "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", - "apiKeysManage": "Gerir Chaves API", + "apiKeysManage": "Gerenciar Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", "apiKeysSettings": "Configurações de {apiKeyName}", - "userTitle": "Gerir Todos os Utilizadores", - "userDescription": "Visualizar e gerir todos os utilizadores no sistema", + "userTitle": "Gerenciar Todos os Usuários", + "userDescription": "Visualizar e gerenciar todos os usuários no sistema", "userAbount": "Sobre a Gestão de Usuário", - "userAbountDescription": "Esta tabela exibe todos os objetos root do utilizador. Cada utilizador pode pertencer a várias organizações. Remover um utilizador de uma organização não exclui seu objeto de utilizador raiz - ele permanecerá no sistema. Para remover completamente um utilizador do sistema, você deve apagar seu objeto raiz usando a ação de apagar nesta tabela.", - "userServer": "Utilizadores do Servidor", - "userSearch": "Pesquisar utilizadores do servidor...", - "userErrorDelete": "Erro ao apagar utilizador", + "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", + "userServer": "Usuários do Servidor", + "userSearch": "Pesquisar usuários do servidor...", + "userErrorDelete": "Erro ao excluir usuário", "userDeleteConfirm": "Confirmar Exclusão do Usuário", - "userDeleteServer": "Excluir utilizador do servidor", - "userMessageRemove": "O utilizador será removido de todas as organizações e será completamente removido do servidor.", - "userMessageConfirm": "Para confirmar, por favor digite o nome do utilizador abaixo.", - "userQuestionRemove": "Tem certeza que deseja apagar o {selectedUser} permanentemente do servidor?", + "userDeleteServer": "Excluir usuário do servidor", + "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", + "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", "valid": "Válido", "numberOfSites": "Número de sites", @@ -314,8 +314,8 @@ "licenseTermsAgree": "Você deve concordar com os termos da licença", "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", - "licenseErrorKeyDelete": "Falha ao apagar chave de licença", - "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao apagar a chave de licença.", + "licenseErrorKeyDelete": "Falha ao excluir chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", "licenseKeyDeleted": "Chave da licença excluída", "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", @@ -336,13 +336,13 @@ "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", - "licenseQuestionRemove": "Tem certeza que deseja apagar a chave de licença {selectedKey}?", + "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", "licenseKeyDelete": "Excluir Chave de Licença", - "licenseKeyDeleteConfirm": "Confirmar que pretende apagar a chave de licença", - "licenseTitle": "Gerir Status da Licença", - "licenseTitleDescription": "Visualizar e gerir chaves de licença no sistema", + "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", + "licenseTitle": "Gerenciar Status da Licença", + "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", "licenseHost": "Licença do host", - "licenseHostDescription": "Gerir a chave de licença principal do host.", + "licenseHostDescription": "Gerenciar a chave de licença principal do host.", "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", @@ -370,37 +370,37 @@ "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", - "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o utilizador novamente mais tarde.", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", "inviteRemoveConfirm": "Confirmar Remoção do Convite", "inviteRegenerated": "Convite Regenerado", "inviteSent": "Um novo convite foi enviado para {email}.", - "inviteSentEmail": "Enviar notificação por e-mail ao utilizador", + "inviteSentEmail": "Enviar notificação por e-mail ao usuário", "inviteGenerate": "Um novo convite foi gerado para {email}.", "inviteDuplicateError": "Convite Duplicado", - "inviteDuplicateErrorDescription": "Já existe um convite para este utilizador.", + "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", "inviteRateLimitError": "Limite de Taxa Excedido", - "inviteRateLimitErrorDescription": "Excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", "inviteRegenerateError": "Falha ao Regenerar Convite", "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", "inviteValidityPeriod": "Período de Validade", "inviteValidityPeriodSelect": "Selecione o período de validade", - "inviteRegenerateMessage": "O convite foi regenerado. O utilizador deve aceder o link abaixo para aceitar o convite.", + "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", "inviteRegenerateButton": "Regenerar", "expiresAt": "Expira em", "accessRoleUnknown": "Função Desconhecida", "placeholder": "Espaço reservado", - "userErrorOrgRemove": "Falha ao remover utilizador", - "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o utilizador.", + "userErrorOrgRemove": "Falha ao remover usuário", + "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", "userOrgRemoved": "Usuário removido", - "userOrgRemovedDescription": "O utilizador {email} foi removido da organização.", + "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", - "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", - "userMessageOrgConfirm": "Para confirmar, digite o nome do utilizador abaixo.", + "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", + "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrg": "Remover Usuário da Organização", - "users": "Utilizadores", + "users": "Usuários", "accessRoleMember": "Membro", "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", @@ -408,7 +408,7 @@ "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", - "usernameRequired": "Nome de utilizador é obrigatório", + "usernameRequired": "Nome de usuário é obrigatório", "idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "accessRoleErrorFetch": "Falha ao buscar funções", @@ -416,51 +416,51 @@ "idpErrorFetch": "Falha ao buscar provedores de identidade", "idpErrorFetchDescription": "Ocorreu um erro ao buscar provedores de identidade", "userErrorExists": "Usuário já existe", - "userErrorExistsDescription": "Este utilizador já é membro da organização.", - "inviteError": "Falha ao convidar utilizador", - "inviteErrorDescription": "Ocorreu um erro ao convidar o utilizador", + "userErrorExistsDescription": "Este usuário já é membro da organização.", + "inviteError": "Falha ao convidar usuário", + "inviteErrorDescription": "Ocorreu um erro ao convidar o usuário", "userInvited": "Usuário convidado", - "userInvitedDescription": "O utilizador foi convidado com sucesso.", - "userErrorCreate": "Falha ao criar utilizador", - "userErrorCreateDescription": "Ocorreu um erro ao criar o utilizador", + "userInvitedDescription": "O usuário foi convidado com sucesso.", + "userErrorCreate": "Falha ao criar usuário", + "userErrorCreateDescription": "Ocorreu um erro ao criar o usuário", "userCreated": "Usuário criado", - "userCreatedDescription": "O utilizador foi criado com sucesso.", + "userCreatedDescription": "O usuário foi criado com sucesso.", "userTypeInternal": "Usuário Interno", - "userTypeInternalDescription": "Convidar um utilizador para se juntar à sua organização diretamente.", + "userTypeInternalDescription": "Convidar um usuário para se juntar à sua organização diretamente.", "userTypeExternal": "Usuário Externo", - "userTypeExternalDescription": "Criar um utilizador com um provedor de identidade externo.", - "accessUserCreateDescription": "Siga os passos abaixo para criar um novo utilizador", - "userSeeAll": "Ver Todos os Utilizadores", + "userTypeExternalDescription": "Criar um usuário com um provedor de identidade externo.", + "accessUserCreateDescription": "Siga os passos abaixo para criar um novo usuário", + "userSeeAll": "Ver Todos os Usuários", "userTypeTitle": "Tipo de Usuário", - "userTypeDescription": "Determine como você deseja criar o utilizador", + "userTypeDescription": "Determine como você deseja criar o usuário", "userSettings": "Informações do Usuário", - "userSettingsDescription": "Insira os detalhes para o novo utilizador", - "inviteEmailSent": "Enviar e-mail de convite para o utilizador", + "userSettingsDescription": "Insira os detalhes para o novo usuário", + "inviteEmailSent": "Enviar e-mail de convite para o usuário", "inviteValid": "Válido Por", "selectDuration": "Selecionar duração", "accessRoleSelect": "Selecionar função", - "inviteEmailSentDescription": "Um e-mail foi enviado ao utilizador com o link de acesso abaixo. Eles devem aceder ao link para aceitar o convite.", - "inviteSentDescription": "O utilizador foi convidado. Eles devem aceder ao link abaixo para aceitar o convite.", + "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", + "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, one {# dia} other {# dias}}.", "idpTitle": "Informações Gerais", - "idpSelect": "Selecione o provedor de identidade para o utilizador externo", - "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar utilizadores externos.", - "usernameUniq": "Isto deve corresponder ao nome de utilizador único que existe no provedor de identidade selecionado.", + "idpSelect": "Selecione o provedor de identidade para o usuário externo", + "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", + "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", "emailOptional": "E-mail (Opcional)", "nameOptional": "Nome (Opcional)", - "accessControls": "Controlos de Acesso", - "userDescription2": "Gerir as configurações deste utilizador", - "accessRoleErrorAdd": "Falha ao adicionar utilizador à função", - "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar utilizador à função.", + "accessControls": "Controles de Acesso", + "userDescription2": "Gerenciar as configurações deste usuário", + "accessRoleErrorAdd": "Falha ao adicionar usuário à função", + "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar usuário à função.", "userSaved": "Usuário salvo", - "userSavedDescription": "O utilizador foi atualizado.", + "userSavedDescription": "O usuário foi atualizado.", "autoProvisioned": "Auto provisionado", - "autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade", - "accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização", - "accessControlsSubmit": "Guardar Controlos de Acesso", + "autoProvisionedDescription": "Permitir que este usuário seja gerenciado automaticamente pelo provedor de identidade", + "accessControlsDescription": "Gerencie o que este usuário pode acessar e fazer na organização", + "accessControlsSubmit": "Salvar Controles de Acesso", "roles": "Funções", - "accessUsersRoles": "Gerir Utilizadores e Funções", - "accessUsersRolesDescription": "Convide utilizadores e adicione-os a funções para gerir o acesso à sua organização", + "accessUsersRoles": "Gerenciar Usuários e Funções", + "accessUsersRolesDescription": "Convide usuários e adicione-os a funções para gerenciar o acesso à sua organização", "key": "Chave", "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", @@ -494,7 +494,7 @@ "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", - "targetTlsSubmit": "Guardar Configurações", + "targetTlsSubmit": "Salvar Configurações", "targets": "Configuração de Alvos", "targetsDescription": "Configure alvos para rotear tráfego para seus serviços de backend", "targetStickySessions": "Ativar Sessões Persistentes", @@ -503,12 +503,12 @@ "targetSubmit": "Adicionar Alvo", "targetNoOne": "Sem alvos. Adicione um alvo usando o formulário.", "targetNoOneDescription": "Adicionar mais de um alvo acima habilitará o balanceamento de carga.", - "targetsSubmit": "Guardar Alvos", + "targetsSubmit": "Salvar Alvos", "proxyAdditional": "Configurações Adicionais de Proxy", "proxyAdditionalDescription": "Configure como seu recurso lida com configurações de proxy", "proxyCustomHeader": "Cabeçalho Host Personalizado", "proxyCustomHeaderDescription": "O cabeçalho host para definir ao fazer proxy de requisições. Deixe vazio para usar o padrão.", - "proxyAdditionalSubmit": "Guardar Configurações de Proxy", + "proxyAdditionalSubmit": "Salvar Configurações de Proxy", "subnetMaskErrorInvalid": "Máscara de subnet inválida. Deve estar entre 0 e 32.", "ipAddressErrorInvalidFormat": "Formato de endereço IP inválido", "ipAddressErrorInvalidOctet": "Octeto de endereço IP inválido", @@ -561,7 +561,7 @@ "ruleSubmit": "Adicionar Regra", "rulesNoOne": "Sem regras. Adicione uma regra usando o formulário.", "rulesOrder": "As regras são avaliadas por prioridade em ordem ascendente.", - "rulesSubmit": "Guardar Regras", + "rulesSubmit": "Salvar Regras", "resourceErrorCreate": "Erro ao criar recurso", "resourceErrorCreateDescription": "Ocorreu um erro ao criar o recurso", "resourceErrorCreateMessage": "Erro ao criar recurso:", @@ -576,7 +576,7 @@ "resourcesDescription": "Recursos são proxies para aplicações executando em sua rede privada. Crie um recurso para qualquer serviço HTTP/HTTPS ou TCP/UDP bruto em sua rede privada. Cada recurso deve estar conectado a um site para habilitar conectividade privada e segura através de um túnel WireGuard criptografado.", "resourcesWireGuardConnect": "Conectividade segura com criptografia WireGuard", "resourcesMultipleAuthenticationMethods": "Configure múltiplos métodos de autenticação", - "resourcesUsersRolesAccess": "Controle de acesso baseado em utilizadores e funções", + "resourcesUsersRolesAccess": "Controle de acesso baseado em usuários e funções", "resourcesErrorUpdate": "Falha ao alternar recurso", "resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso", "access": "Acesso", @@ -606,7 +606,7 @@ "pangolinSettings": "Configurações - Pangolin", "accessRoleYour": "Sua função:", "accessRoleSelect2": "Selecionar uma função", - "accessUserSelect": "Selecionar um utilizador", + "accessUserSelect": "Selecionar um usuário", "otpEmailEnter": "Digite um e-mail", "otpEmailEnterDescription": "Pressione enter para adicionar um e-mail após digitá-lo no campo de entrada.", "otpEmailErrorInvalid": "Endereço de e-mail inválido. O caractere curinga (*) deve ser a parte local inteira.", @@ -616,8 +616,8 @@ "otpEmailTitleDescription": "Requer autenticação baseada em e-mail para acesso ao recurso", "otpEmailWhitelist": "Lista de E-mails Permitidos", "otpEmailWhitelistList": "E-mails na Lista Permitida", - "otpEmailWhitelistListDescription": "Apenas utilizadores com estes endereços de e-mail poderão aceder este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", - "otpEmailWhitelistSave": "Guardar Lista Permitida", + "otpEmailWhitelistListDescription": "Apenas usuários com estes endereços de e-mail poderão acessar este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", + "otpEmailWhitelistSave": "Salvar Lista Permitida", "passwordAdd": "Adicionar Senha", "passwordRemove": "Remover Senha", "pincodeAdd": "Adicionar Código PIN", @@ -657,14 +657,14 @@ "resourcePincodeSetupDescription": "O código PIN do recurso foi definido com sucesso", "resourcePincodeSetupTitle": "Definir Código PIN", "resourcePincodeSetupTitleDescription": "Defina um código PIN para proteger este recurso", - "resourceRoleDescription": "Administradores sempre podem aceder este recurso.", - "resourceUsersRoles": "Utilizadores e Funções", - "resourceUsersRolesDescription": "Configure quais utilizadores e funções podem visitar este recurso", - "resourceUsersRolesSubmit": "Guardar Utilizadores e Funções", + "resourceRoleDescription": "Administradores sempre podem acessar este recurso.", + "resourceUsersRoles": "Usuários e Funções", + "resourceUsersRolesDescription": "Configure quais usuários e funções podem visitar este recurso", + "resourceUsersRolesSubmit": "Salvar Usuários e Funções", "resourceWhitelistSave": "Salvo com sucesso", "resourceWhitelistSaveDescription": "As configurações da lista permitida foram salvas", "ssoUse": "Usar SSO da Plataforma", - "ssoUseDescription": "Os utilizadores existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", + "ssoUseDescription": "Os usuários existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", "proxyErrorInvalidPort": "Número da porta inválido", "subdomainErrorInvalid": "Subdomínio inválido", "domainErrorFetch": "Erro ao buscar domínios", @@ -690,7 +690,7 @@ "siteDestination": "Site de Destino", "searchSites": "Pesquisar sites", "accessRoleCreate": "Criar Função", - "accessRoleCreateDescription": "Crie uma nova função para agrupar utilizadores e gerir suas permissões.", + "accessRoleCreateDescription": "Crie uma nova função para agrupar usuários e gerenciar suas permissões.", "accessRoleCreateSubmit": "Criar Função", "accessRoleCreated": "Função criada", "accessRoleCreatedDescription": "A função foi criada com sucesso.", @@ -700,13 +700,13 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a apagar a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", "accessRoleRemoved": "Função removida", "accessRoleRemovedDescription": "A função foi removida com sucesso.", - "accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.", + "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", @@ -918,8 +918,8 @@ "idpAzureAlt": "Azure", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", - "inviteErrorWrongUser": "O convite não é para este utilizador", - "inviteErrorUserNotExists": "O utilizador não existe. Por favor, crie uma conta primeiro.", + "inviteErrorWrongUser": "O convite não é para este usuário", + "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", "inviteErrorExpired": "O convite pode ter expirado", "inviteErrorRevoked": "O convite pode ter sido revogado", @@ -934,7 +934,7 @@ "home": "Início", "accessControl": "Controle de Acesso", "settings": "Configurações", - "usersAll": "Todos os Utilizadores", + "usersAll": "Todos os Usuários", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", "noResults": "Nenhum resultado encontrado.", @@ -987,8 +987,8 @@ "licenseTierProfessionalRequired": "Edição Profissional Necessária", "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", "actionGetOrg": "Obter Organização", - "updateOrgUser": "Atualizar utilizador Org", - "createOrgUser": "Criar utilizador Org", + "updateOrgUser": "Atualizar usuário Org", + "createOrgUser": "Criar usuário Org", "actionUpdateOrg": "Atualizar Organização", "actionUpdateUser": "Atualizar Usuário", "actionGetUser": "Obter Usuário", @@ -1135,8 +1135,8 @@ "sidebarRoles": "Papéis", "sidebarShareableLinks": "Links compartilháveis", "sidebarApiKeys": "Chaves API", - "sidebarSettings": "Configurações", - "sidebarAllUsers": "Todos os utilizadores", + "sidebarSettings": "Confirgurações", + "sidebarAllUsers": "Todos os usuários", "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "sidebarClients": "Clientes (Beta)", @@ -1189,7 +1189,7 @@ "loading": "Carregando", "restart": "Reiniciar", "domains": "Domínios", - "domainsDescription": "Gerir domínios para sua organização", + "domainsDescription": "Gerencie domínios para sua organização", "domainsSearch": "Pesquisar domínios...", "domainAdd": "Adicionar Domínio", "domainAddDescription": "Registre um novo domínio com sua organização", @@ -1217,7 +1217,7 @@ "pending": "Pendente", "sidebarBilling": "Faturamento", "billing": "Faturamento", - "orgBillingDescription": "Gerir suas informações de faturação e assinaturas", + "orgBillingDescription": "Gerencie suas informações de faturamento e assinaturas", "github": "GitHub", "pangolinHosted": "Hospedagem Pangolin", "fossorial": "Fossorial", @@ -1232,7 +1232,7 @@ "completeSetup": "Configuração Completa", "accountSetupSuccess": "Configuração da conta concluída! Bem-vindo ao Pangolin!", "documentation": "Documentação", - "saveAllSettings": "Guardar Todas as Configurações", + "saveAllSettings": "Salvar Todas as Configurações", "settingsUpdated": "Configurações atualizadas", "settingsUpdatedDescription": "Todas as configurações foram atualizadas com sucesso", "settingsErrorUpdate": "Falha ao atualizar configurações", @@ -1263,7 +1263,7 @@ "createNewOrgDescription": "Crie uma nova organização", "organization": "Organização", "port": "Porta", - "securityKeyManage": "Gerir chaves de segurança", + "securityKeyManage": "Gerenciar chaves de segurança", "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", "securityKeyRegister": "Registrar nova chave de segurança", "securityKeyList": "Suas chaves de segurança", @@ -1314,7 +1314,7 @@ "createDomainARecords": "Registros A", "createDomainRecordNumber": "Registrar {number}", "createDomainTxtRecords": "Registros TXT", - "createDomainSaveTheseRecords": "Guardar Esses Registros", + "createDomainSaveTheseRecords": "Salvar Esses Registros", "createDomainSaveTheseRecordsDescription": "Certifique-se de salvar esses registros DNS, pois você não os verá novamente.", "createDomainDnsPropagation": "Propagação DNS", "createDomainDnsPropagationDescription": "Alterações no DNS podem levar algum tempo para se propagar pela internet. Pode levar de alguns minutos a 48 horas, dependendo do seu provedor de DNS e das configurações de TTL.", @@ -1401,7 +1401,7 @@ "editInternalResourceDialogSitePort": "Porta do Site", "editInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "editInternalResourceDialogCancel": "Cancelar", - "editInternalResourceDialogSaveResource": "Guardar Recurso", + "editInternalResourceDialogSaveResource": "Salvar Recurso", "editInternalResourceDialogSuccess": "Sucesso", "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno atualizado com sucesso", "editInternalResourceDialogError": "Erro", @@ -1428,7 +1428,7 @@ "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", "createInternalResourceDialogSitePort": "Porta do Site", - "createInternalResourceDialogSitePortDescription": "Use esta porta para aceder o recurso no site quando conectado com um cliente.", + "createInternalResourceDialogSitePortDescription": "Use esta porta para acessar o recurso no site quando conectado com um cliente.", "createInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "createInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "createInternalResourceDialogDestinationPortDescription": "A porta no IP de destino onde o recurso está acessível.", @@ -1452,7 +1452,7 @@ "siteAddress": "Endereço do Site", "siteAddressDescription": "Especificar o endereço IP do host para que os clientes se conectem. Este é o endereço interno do site na rede Pangolin para os clientes endereçarem. Deve estar dentro da sub-rede da Organização.", "autoLoginExternalIdp": "Login Automático com IDP Externo", - "autoLoginExternalIdpDescription": "Redirecionar imediatamente o utilizador para o IDP externo para autenticação.", + "autoLoginExternalIdpDescription": "Redirecionar imediatamente o usuário para o IDP externo para autenticação.", "selectIdp": "Selecionar IDP", "selectIdpPlaceholder": "Escolher um IDP...", "selectIdpRequired": "Por favor, selecione um IDP quando o login automático estiver ativado.", @@ -1479,7 +1479,7 @@ }, "benefitLessMaintenance": { "title": "Menos manutenção", - "description": "Sem migrações, backups ou infraestrutura extra para gerir. Lidamos com isso na nuvem." + "description": "Sem migrações, backups ou infraestrutura extra para gerenciar. Lidamos com isso na nuvem." }, "benefitCloudFailover": { "title": "Falha na nuvem", @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Provedor Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Cabeçalhos Personalizados", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Cabeçalhos devem estar no formato: Nome do Cabeçalho: valor.", "domainPickerProvidedDomain": "Domínio fornecido", "domainPickerFreeProvidedDomain": "Domínio fornecido grátis", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui." -} +} \ No newline at end of file From 75747268156587da03afad8f6522177918a10140 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:47 -0700 Subject: [PATCH 096/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 94e93594..07395fcb 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC провайдер", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Пользовательские заголовки", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.", "domainPickerProvidedDomain": "Домен предоставлен", "domainPickerFreeProvidedDomain": "Бесплатный домен", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда." -} +} \ No newline at end of file From 21811465b637db7518d29057c73cad489d7ec689 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:48 -0700 Subject: [PATCH 097/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index cf1c9157..33ee83c3 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı", "customHeaders": "Özel Başlıklar", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.", "domainPickerProvidedDomain": "Sağlanan Alan Adı", "domainPickerFreeProvidedDomain": "Ücretsiz Sağlanan Alan Adı", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün." -} +} \ No newline at end of file From 39851c34125607a5b05c888d5871f15379d76038 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:50 -0700 Subject: [PATCH 098/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 656096e9..abaa2c53 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC 提供商", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "自定义标题", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "头部必须是格式:头部名称:值。", "domainPickerProvidedDomain": "提供的域", "domainPickerFreeProvidedDomain": "免费提供的域", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。" -} +} \ No newline at end of file From 21fc8297667225343e8b9b99a3aaf6805324a891 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 01:02:51 -0700 Subject: [PATCH 099/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index aca7fe6d..a03dc629 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC leverandør", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Egendefinerte topptekster", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Topptekst må være i formatet: header-navn: verdi.", "domainPickerProvidedDomain": "Gitt domene", "domainPickerFreeProvidedDomain": "Gratis oppgitt domene", @@ -1522,4 +1523,4 @@ "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her." -} +} \ No newline at end of file From b5e04e8111c11d0545ddc3dbb79de8e53e597123 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 1 Oct 2025 09:49:29 -0700 Subject: [PATCH 100/322] Add exit node name --- server/routers/client/updateClient.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index dae80732..5adbfa01 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -280,6 +280,7 @@ export async function updateClient( reachableAt: string; exitNodeId: number; type: string; + name: string; sourceIp: string; sourcePort: number; destinations: PeerDestination[]; @@ -310,6 +311,7 @@ export async function updateClient( reachableAt: site.exitNodes?.reachableAt || "", exitNodeId: site.exitNodes?.exitNodeId || 0, type: site.exitNodes?.type || "", + name: site.exitNodes?.name || "", sourceIp: site.clientSites.endpoint.split(":")[0] || "", sourcePort: parseInt(site.clientSites.endpoint.split(":")[1]) || 0, @@ -352,7 +354,8 @@ export async function updateClient( const exitNodeForComm = { exitNodeId: destination.exitNodeId, type: destination.type, - reachableAt: destination.reachableAt + reachableAt: destination.reachableAt, + name: destination.name } as any; // Using 'as any' since we know sendToExitNode will handle this correctly await sendToExitNode(exitNodeForComm, { From 204fdfd233c71703bf0803b24d7a44e8b774c445 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 12:45:15 -0700 Subject: [PATCH 101/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 3c8de364..c3c2a925 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -3,14 +3,14 @@ "setupNewOrg": "Nieuwe organisatie", "setupCreateOrg": "Nieuwe organisatie aanmaken", "setupCreateResources": "Bronnen aanmaken", - "setupOrgName": "Naam organisatie", + "setupOrgName": "Naam van de organisatie", "orgDisplayName": "Dit is de weergavenaam van uw organisatie.", "orgId": "Organisatie ID", "setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.", "setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.", "componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.", "componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.", - "welcome": "Welkom bij Pangolin", + "welcome": "Welkom bij Pangolin!", "welcomeTo": "Welkom bij", "componentsCreateOrg": "Maak een Organisatie", "componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.", @@ -22,7 +22,7 @@ "inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.", "inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.", "inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.", - "inviteCreateUser": "U moet eerst een account aanmaken", + "inviteCreateUser": "U moet eerst een account aanmaken.", "goHome": "Ga naar huis", "inviteLogInOtherUser": "Log in als een andere gebruiker", "createAnAccount": "Account aanmaken", @@ -35,15 +35,15 @@ "createAccount": "Account Aanmaken", "viewSettings": "Instellingen weergeven", "delete": "Verwijderen", - "name": "naam", + "name": "Naam", "online": "Online", "offline": "Offline", - "site": "Website", - "dataIn": "Gegevens in", - "dataOut": "Data Uit", + "site": "Referentie", + "dataIn": "Dataverbruik inkomend", + "dataOut": "Dataverbruik uitgaand", "connectionType": "Type verbinding", "tunnelType": "Tunnel type", - "local": "lokaal", + "local": "Lokaal", "edit": "Bewerken", "siteConfirmDelete": "Verwijderen van site bevestigen", "siteDelete": "Site verwijderen", @@ -55,7 +55,7 @@ "siteCreate": "Site maken", "siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden", "siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen", - "close": "Afsluiten", + "close": "Sluiten", "siteErrorCreate": "Fout bij maken site", "siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden", "siteErrorCreateDefaults": "Standaardinstellingen niet gevonden", @@ -90,7 +90,7 @@ "siteGeneralDescription": "Algemene instellingen voor deze site configureren", "siteSettingDescription": "Configureer de instellingen op uw site", "siteSetting": "{siteName} instellingen", - "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", + "siteNewtTunnel": "Newttunnel (Aanbevolen)", "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", @@ -104,7 +104,7 @@ "siteCredentialsSave": "Uw referenties opslaan", "siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "siteInfo": "Site informatie", - "status": "status", + "status": "Status", "shareTitle": "Beheer deellinks", "shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen", "shareSearch": "Zoek share links...", @@ -152,13 +152,13 @@ "resourceErrorDelte": "Fout bij verwijderen document", "authentication": "Authenticatie", "protected": "Beschermd", - "notProtected": "Niet beschermd", + "notProtected": "Niet beveiligd", "resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.", "resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.", "resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?", "resourceHTTP": "HTTPS bron", "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", - "resourceRaw": "Ruwe TCP/UDP bron", + "resourceRaw": "TCP/UDP bron", "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", "resourceCreate": "Bron maken", "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", @@ -183,7 +183,7 @@ "protocolSelect": "Selecteer een protocol", "resourcePortNumber": "Nummer van poort", "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", - "cancel": "annuleren", + "cancel": "Annuleren", "resourceConfig": "Configuratie tekstbouwstenen", "resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen", "resourceAddEntrypoints": "Traefik: Entrypoints toevoegen", @@ -212,7 +212,7 @@ "saveGeneralSettings": "Algemene instellingen opslaan", "saveSettings": "Instellingen opslaan", "orgDangerZone": "Gevaarlijke zone", - "orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.", + "orgDangerZoneDescription": "Deze instantie verwijderen is onomkeerbaar. Bevestig alstublieft dat u wilt doorgaan.", "orgDelete": "Verwijder organisatie", "orgDeleteConfirm": "Bevestig Verwijderen Organisatie", "orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.", @@ -501,7 +501,7 @@ "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", "methodSelect": "Selecteer methode", "targetSubmit": "Doelwit toevoegen", - "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", + "targetNoOne": "Geen doel toegevoegd. Voeg deze toe via dit formulier.", "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", "targetsSubmit": "Doelstellingen opslaan", "proxyAdditional": "Extra Proxy-instellingen", @@ -598,7 +598,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", - "sites": "Werkruimtes", + "sites": "Verbindingen", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", @@ -1504,7 +1504,7 @@ "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Aangepaste headers", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", + "customHeadersDescription": "Voeg aangepaste headers toe die met proxyverzoeken worden meegestuurd. Gebruik één regel per header in het formaat 'Header-naam: waarde'", "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", "domainPickerProvidedDomain": "Opgegeven domein", "domainPickerFreeProvidedDomain": "Gratis verstrekt domein", From 38f212d6322d053cde88633eb148b8d4b28b8688 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 1 Oct 2025 14:02:59 -0700 Subject: [PATCH 102/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index c3c2a925..e1e95e49 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -97,7 +97,7 @@ "siteWgDescriptionSaas": "Gebruik elke WireGuard-client om een tunnel op te zetten. Handmatige NAT-instelling vereist. WERKT ALLEEN OP SELF HOSTED NODES", "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", "siteLocalDescriptionSaas": "Alleen lokale bronnen. Geen tunneling. WERKT ALLEEN OP SELF HOSTED NODES", - "siteSeeAll": "Alle werkruimtes bekijken", + "siteSeeAll": "Alle sites bekijken", "siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site", "siteNewtCredentials": "Nieuwste aanmeldgegevens", "siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server", @@ -146,7 +146,7 @@ "never": "Nooit", "shareErrorSelectResource": "Selecteer een bron", "resourceTitle": "Bronnen beheren", - "resourceDescription": "Veilige proxy's voor uw privé applicaties maken", + "resourceDescription": "Veilige proxy's voor uw privéapplicaties maken", "resourcesSearch": "Zoek bronnen...", "resourceAdd": "Bron toevoegen", "resourceErrorDelte": "Fout bij verwijderen document", @@ -572,7 +572,7 @@ "domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen", "none": "geen", "unknown": "onbekend", - "resources": "Hulpmiddelen", + "resources": "Bronnen", "resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.", "resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling", "resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren", @@ -598,7 +598,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", - "sites": "Verbindingen", + "sites": "Sites", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", @@ -727,22 +727,22 @@ "idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?", "idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.", "idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.", - "idpConfirmDelete": "Bevestig verwijderen Identity Provider", - "idpDelete": "Identity Provider verwijderen", - "idp": "Identiteit aanbieders", - "idpSearch": "Identiteitsaanbieders zoeken...", - "idpAdd": "Identity Provider toevoegen", - "idpClientIdRequired": "Client-ID is vereist.", - "idpClientSecretRequired": "Clientgeheim is vereist.", - "idpErrorAuthUrlInvalid": "Authenticatie-URL moet een geldige URL zijn.", - "idpErrorTokenUrlInvalid": "Token-URL moet een geldige URL zijn.", + "idpConfirmDelete": "Bevestig verwijderen identiteit provider", + "idpDelete": "Identiteit provider verwijderen", + "idp": "Identiteitsproviders", + "idpSearch": "Identiteitsproviders zoeken...", + "idpAdd": "Identiteit provider toevoegen", + "idpClientIdRequired": "Client ID is vereist.", + "idpClientSecretRequired": "Client geheim is vereist.", + "idpErrorAuthUrlInvalid": "Authenticatie URL moet een geldige URL zijn.", + "idpErrorTokenUrlInvalid": "Token URL moet een geldige URL zijn.", "idpPathRequired": "ID-pad is vereist.", "idpScopeRequired": "Toepassingsgebieden zijn vereist.", - "idpOidcDescription": "Een OpenID Connect identity provider configureren", - "idpCreatedDescription": "Identity provider succesvol aangemaakt", - "idpCreate": "Identity Provider aanmaken", - "idpCreateDescription": "Een nieuwe identiteitsprovider voor gebruikersauthenticatie configureren", - "idpSeeAll": "Zie alle identiteitsaanbieders", + "idpOidcDescription": "Een OpenID Connect identiteitsprovider configureren", + "idpCreatedDescription": "Identiteitsprovider succesvol aangemaakt", + "idpCreate": "Identiteitsprovider aanmaken", + "idpCreateDescription": "Een nieuwe identiteitsprovider voor authenticatie configureren", + "idpSeeAll": "Zie alle Identiteitsproviders", "idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider", "idpDisplayName": "Een weergavenaam voor deze identiteitsprovider", "idpAutoProvisionUsers": "Auto Provisie Gebruikers", @@ -752,10 +752,10 @@ "idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren", "idpOidcConfigure": "OAuth2/OIDC configuratie", "idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties", - "idpClientId": "Klant ID", - "idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider", - "idpClientSecret": "Clientgeheim", - "idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider", + "idpClientId": "Client ID", + "idpClientIdDescription": "De OAuth2 client ID van uw identiteitsprovider", + "idpClientSecret": "Client Secret", + "idpClientSecretDescription": "Het OAuth2 Client Secret van je identiteitsprovider", "idpAuthUrl": "URL autorisatie", "idpAuthUrlDescription": "De URL voor autorisatie OAuth2", "idpTokenUrl": "URL token", @@ -801,7 +801,7 @@ "defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.", "defaultMappingsSubmit": "Standaard toewijzingen opslaan", "orgPoliciesEdit": "Organisatie beleid bewerken", - "org": "Rekening", + "org": "Organisatie", "orgSelect": "Selecteer organisatie", "orgSearch": "Zoek in org", "orgNotFound": "Geen org gevonden.", @@ -976,10 +976,10 @@ "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", "githubUsername": "GitHub-gebruikersnaam", "supportKeyInput": "Supporter Sleutel", - "supportKeyBuy": "Koop Supportersleutel", + "supportKeyBuy": "Koop supportersleutel", "logoutError": "Fout bij uitloggen", "signingAs": "Ingelogd als", - "serverAdmin": "Server Beheerder", + "serverAdmin": "Server beheer", "managedSelfhosted": "Beheerde Self-Hosted", "otpEnable": "Twee-factor inschakelen", "otpDisable": "Tweestapsverificatie uitschakelen", @@ -1128,7 +1128,7 @@ "sidebarOverview": "Overzicht.", "sidebarHome": "Startpagina", "sidebarSites": "Werkruimtes", - "sidebarResources": "Hulpmiddelen", + "sidebarResources": "Bronnen", "sidebarAccessControl": "Toegangs controle", "sidebarUsers": "Gebruikers", "sidebarInvitations": "Uitnodigingen", From a49d900951869041e7345d7322aae991161de75c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 01:18:15 +0000 Subject: [PATCH 103/322] Bump the dev-patch-updates group across 1 directory with 4 updates Bumps the dev-patch-updates group with 3 updates in the / directory: [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [typescript](https://github.com/microsoft/TypeScript). Updates `@tailwindcss/postcss` from 4.1.13 to 4.1.14 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.14/packages/@tailwindcss-postcss) Updates `@types/node` from 24.6.0 to 24.6.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `tailwindcss` from 4.1.13 to 4.1.14 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.14/packages/tailwindcss) Updates `typescript` from 5.9.2 to 5.9.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.2...v5.9.3) --- updated-dependencies: - dependency-name: "@tailwindcss/postcss" dependency-version: 4.1.14 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@types/node" dependency-version: 24.6.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: tailwindcss dependency-version: 4.1.14 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: typescript dependency-version: 5.9.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 219 +++++++++++++++++++++------------------------- package.json | 4 +- 2 files changed, 103 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72ae8583..9bf506ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,7 +99,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@tailwindcss/postcss": "^4.1.13", + "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", "@types/cors": "2.8.19", @@ -109,7 +109,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.0", + "@types/node": "24.6.1", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", @@ -5895,54 +5895,54 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", - "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", + "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", - "jiti": "^2.5.1", + "jiti": "^2.6.0", "lightningcss": "1.30.1", - "magic-string": "^0.30.18", + "magic-string": "^0.30.19", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.14" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", - "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz", + "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "detect-libc": "^2.0.4", - "tar": "^7.4.3" + "tar": "^7.5.1" }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-x64": "4.1.13", - "@tailwindcss/oxide-freebsd-x64": "4.1.13", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-x64-musl": "4.1.13", - "@tailwindcss/oxide-wasm32-wasi": "4.1.13", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + "@tailwindcss/oxide-android-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-x64": "4.1.14", + "@tailwindcss/oxide-freebsd-x64": "4.1.14", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-x64-musl": "4.1.14", + "@tailwindcss/oxide-wasm32-wasi": "4.1.14", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", - "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz", + "integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==", "cpu": [ "arm64" ], @@ -5957,9 +5957,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", - "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz", + "integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==", "cpu": [ "arm64" ], @@ -5974,9 +5974,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", - "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz", + "integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==", "cpu": [ "x64" ], @@ -5991,9 +5991,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", - "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz", + "integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==", "cpu": [ "x64" ], @@ -6008,9 +6008,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", - "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz", + "integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==", "cpu": [ "arm" ], @@ -6025,9 +6025,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", - "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz", + "integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==", "cpu": [ "arm64" ], @@ -6042,9 +6042,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", - "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz", + "integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==", "cpu": [ "arm64" ], @@ -6059,9 +6059,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", - "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz", + "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", "cpu": [ "x64" ], @@ -6076,9 +6076,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", - "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz", + "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", "cpu": [ "x64" ], @@ -6093,9 +6093,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", - "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz", + "integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -6111,30 +6111,30 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.5", - "@emnapi/runtime": "^1.4.5", - "@emnapi/wasi-threads": "^1.0.4", - "@napi-rs/wasm-runtime": "^0.2.12", - "@tybys/wasm-util": "^0.10.0", - "tslib": "^2.8.0" + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.5", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.4.5", + "version": "1.5.0", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.4", + "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.4.5", + "version": "1.5.0", "dev": true, "inBundle": true, "license": "MIT", @@ -6144,7 +6144,7 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", + "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT", @@ -6154,19 +6154,19 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", + "version": "1.0.5", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.0", + "version": "0.10.1", "dev": true, "inBundle": true, "license": "MIT", @@ -6176,16 +6176,16 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.0", + "version": "2.8.1", "dev": true, "inBundle": true, "license": "0BSD", "optional": true }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", - "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz", + "integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==", "cpu": [ "arm64" ], @@ -6200,9 +6200,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", - "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz", + "integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==", "cpu": [ "x64" ], @@ -6217,17 +6217,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz", - "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.14.tgz", + "integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.13", - "@tailwindcss/oxide": "4.1.13", + "@tailwindcss/node": "4.1.14", + "@tailwindcss/oxide": "4.1.14", "postcss": "^8.4.41", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.14" } }, "node_modules/@tanstack/react-table": { @@ -6431,9 +6431,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.0.tgz", - "integrity": "sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==", + "version": "24.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", + "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -11144,9 +11144,9 @@ } }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "devOptional": true, "license": "MIT", "bin": { @@ -11948,9 +11948,9 @@ } }, "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -11960,22 +11960,6 @@ "node": ">= 18" } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -17460,9 +17444,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", - "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", + "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", "license": "MIT" }, "node_modules/tapable": { @@ -17480,17 +17464,16 @@ } }, "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", "dev": true, "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -17885,9 +17868,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 405157b8..0fec96cd 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@tailwindcss/postcss": "^4.1.13", + "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", "@types/cors": "2.8.19", @@ -126,7 +126,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.0", + "@types/node": "24.6.1", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", From 756fcbb590db8acacecd331c36357898a89478d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 01:21:48 +0000 Subject: [PATCH 104/322] Bump the prod-patch-updates group across 1 directory with 5 updates Bumps the prod-patch-updates group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) | `0.5.3` | `0.5.5` | | [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) | `0.44.5` | `0.44.6` | | [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `15.5.3` | `15.5.4` | | [next](https://github.com/vercel/next.js) | `15.5.3` | `15.5.4` | | [npm](https://github.com/npm/cli) | `11.6.0` | `11.6.1` | Updates `@react-email/components` from 0.5.3 to 0.5.5 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/components/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/components@0.5.5/packages/components) Updates `drizzle-orm` from 0.44.5 to 0.44.6 - [Release notes](https://github.com/drizzle-team/drizzle-orm/releases) - [Commits](https://github.com/drizzle-team/drizzle-orm/compare/0.44.5...0.44.6) Updates `eslint-config-next` from 15.5.3 to 15.5.4 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v15.5.4/packages/eslint-config-next) Updates `next` from 15.5.3 to 15.5.4 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.3...v15.5.4) Updates `npm` from 11.6.0 to 11.6.1 - [Release notes](https://github.com/npm/cli/releases) - [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md) - [Commits](https://github.com/npm/cli/compare/v11.6.0...v11.6.1) --- updated-dependencies: - dependency-name: "@react-email/components" dependency-version: 0.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: drizzle-orm dependency-version: 0.44.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: eslint-config-next dependency-version: 15.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: next dependency-version: 15.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: npm dependency-version: 11.6.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 1010 ++++++++++++++++++++++----------------------- package.json | 10 +- 2 files changed, 498 insertions(+), 522 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72ae8583..bd89a8ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-tooltip": "^1.2.8", - "@react-email/components": "0.5.3", + "@react-email/components": "0.5.5", "@react-email/render": "^1.2.0", "@react-email/tailwind": "1.2.2", "@simplewebauthn/browser": "^13.2.0", @@ -51,9 +51,9 @@ "cookies": "^0.9.1", "cors": "2.8.5", "crypto-js": "^4.2.0", - "drizzle-orm": "0.44.5", + "drizzle-orm": "0.44.6", "eslint": "9.35.0", - "eslint-config-next": "15.5.3", + "eslint-config-next": "15.5.4", "express": "5.1.0", "express-rate-limit": "8.1.0", "glob": "11.0.3", @@ -66,13 +66,13 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.544.0", "moment": "2.30.1", - "next": "15.5.3", + "next": "15.5.4", "next-intl": "^4.3.9", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.6", - "npm": "^11.6.0", + "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", "posthog-node": "^5.8.4", @@ -2842,24 +2842,24 @@ } }, "node_modules/@next/env": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", - "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.4.tgz", + "integrity": "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.3.tgz", - "integrity": "sha512-SdhaKdko6dpsSr0DldkESItVrnPYB1NS2NpShCSX5lc7SSQmLZt5Mug6t2xbiuVWEVDLZSuIAoQyYVBYp0dR5g==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.4.tgz", + "integrity": "sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==", "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz", - "integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.4.tgz", + "integrity": "sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==", "cpu": [ "arm64" ], @@ -2873,9 +2873,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz", - "integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.4.tgz", + "integrity": "sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==", "cpu": [ "x64" ], @@ -2889,9 +2889,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz", - "integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.4.tgz", + "integrity": "sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==", "cpu": [ "arm64" ], @@ -2905,9 +2905,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz", - "integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.4.tgz", + "integrity": "sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==", "cpu": [ "arm64" ], @@ -2921,9 +2921,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.3.tgz", - "integrity": "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz", + "integrity": "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==", "cpu": [ "x64" ], @@ -2937,9 +2937,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.3.tgz", - "integrity": "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz", + "integrity": "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==", "cpu": [ "x64" ], @@ -2953,9 +2953,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz", - "integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.4.tgz", + "integrity": "sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==", "cpu": [ "arm64" ], @@ -2969,9 +2969,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz", - "integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.4.tgz", + "integrity": "sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==", "cpu": [ "x64" ], @@ -4941,9 +4941,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.3.tgz", - "integrity": "sha512-8G5vsoMehuGOT4cDqaYLdpagtqCYPl4vThXNylClxO6SrN2w9Mh1+i2RNGj/rdqh/woamHORjlXMYCA/kzDMew==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.5.tgz", + "integrity": "sha512-+utnip1DiXTq5TQKvL8qztWy0EC3L+qdRIeJkBZXJA4WGIukbaqimWCTBGIMW19Pj+1iKvDYk2JQHEQDLiq7uQ==", "license": "MIT", "dependencies": { "@react-email/body": "0.1.0", @@ -4961,7 +4961,7 @@ "@react-email/link": "0.0.12", "@react-email/markdown": "0.0.15", "@react-email/preview": "0.0.13", - "@react-email/render": "1.2.3", + "@react-email/render": "1.3.1", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", "@react-email/tailwind": "1.2.2", @@ -4974,6 +4974,24 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, + "node_modules/@react-email/components/node_modules/@react-email/render": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.1.tgz", + "integrity": "sha512-BOc/kanieEVyiuldFFvceriiBGBBVhe4JWWXCXE2ehLIqz+gSWD4rgCoXAGbJRnnVyyp4joPqK62bSfa88yonA==", + "license": "MIT", + "dependencies": { + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, "node_modules/@react-email/container": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", @@ -8534,9 +8552,9 @@ } }, "node_modules/drizzle-orm": { - "version": "0.44.5", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.5.tgz", - "integrity": "sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ==", + "version": "0.44.6", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.6.tgz", + "integrity": "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ==", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", @@ -9223,12 +9241,12 @@ } }, "node_modules/eslint-config-next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.3.tgz", - "integrity": "sha512-e6j+QhQFOr5pfsc8VJbuTD9xTXJaRvMHYjEeLPA2pFkheNlgPLCkxdvhxhfuM4KGcqSZj2qEnpHisdTVs3BxuQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.4.tgz", + "integrity": "sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==", "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.5.3", + "@next/eslint-plugin-next": "15.5.4", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -12066,12 +12084,12 @@ } }, "node_modules/next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", - "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz", + "integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==", "license": "MIT", "dependencies": { - "@next/env": "15.5.3", + "@next/env": "15.5.4", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -12084,14 +12102,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.3", - "@next/swc-darwin-x64": "15.5.3", - "@next/swc-linux-arm64-gnu": "15.5.3", - "@next/swc-linux-arm64-musl": "15.5.3", - "@next/swc-linux-x64-gnu": "15.5.3", - "@next/swc-linux-x64-musl": "15.5.3", - "@next/swc-win32-arm64-msvc": "15.5.3", - "@next/swc-win32-x64-msvc": "15.5.3", + "@next/swc-darwin-arm64": "15.5.4", + "@next/swc-darwin-x64": "15.5.4", + "@next/swc-linux-arm64-gnu": "15.5.4", + "@next/swc-linux-arm64-musl": "15.5.4", + "@next/swc-linux-x64-gnu": "15.5.4", + "@next/swc-linux-x64-musl": "15.5.4", + "@next/swc-win32-arm64-msvc": "15.5.4", + "@next/swc-win32-x64-msvc": "15.5.4", "sharp": "^0.34.3" }, "peerDependencies": { @@ -12264,9 +12282,9 @@ } }, "node_modules/npm": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.0.tgz", - "integrity": "sha512-d/P7DbvYgYNde9Ehfeq99+13/E7E82PfZPw8uYZASr9sQ3ZhBBCA9cXSJRA1COfJ6jDLJ0K36UJnXQWhCvLXuQ==", + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", + "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -12345,57 +12363,57 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.4", - "@npmcli/config": "^10.4.0", + "@npmcli/arborist": "^9.1.5", + "@npmcli/config": "^10.4.1", "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^4.0.2", - "@npmcli/package-json": "^6.2.0", - "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.1", + "@npmcli/promise-spawn": "^8.0.3", "@npmcli/redact": "^3.2.2", - "@npmcli/run-script": "^9.1.0", - "@sigstore/tuf": "^3.1.1", + "@npmcli/run-script": "^10.0.0", + "@sigstore/tuf": "^4.0.0", "abbrev": "^3.0.1", "archy": "~1.0.0", - "cacache": "^19.0.1", - "chalk": "^5.4.1", + "cacache": "^20.0.1", + "chalk": "^5.6.2", "ci-info": "^4.3.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.4.5", + "glob": "^11.0.3", "graceful-fs": "^4.2.11", - "hosted-git-info": "^8.1.0", + "hosted-git-info": "^9.0.0", "ini": "^5.0.0", - "init-package-json": "^8.2.1", - "is-cidr": "^5.1.1", + "init-package-json": "^8.2.2", + "is-cidr": "^6.0.0", "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.1", - "libnpmdiff": "^8.0.7", - "libnpmexec": "^10.1.6", - "libnpmfund": "^7.0.7", - "libnpmorg": "^8.0.0", - "libnpmpack": "^9.0.7", - "libnpmpublish": "^11.1.0", - "libnpmsearch": "^9.0.0", - "libnpmteam": "^8.0.1", - "libnpmversion": "^8.0.1", - "make-fetch-happen": "^14.0.3", - "minimatch": "^9.0.5", + "libnpmaccess": "^10.0.2", + "libnpmdiff": "^8.0.8", + "libnpmexec": "^10.1.7", + "libnpmfund": "^7.0.8", + "libnpmorg": "^8.0.1", + "libnpmpack": "^9.0.8", + "libnpmpublish": "^11.1.1", + "libnpmsearch": "^9.0.1", + "libnpmteam": "^8.0.2", + "libnpmversion": "^8.0.2", + "make-fetch-happen": "^15.0.2", + "minimatch": "^10.0.3", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^11.2.0", + "node-gyp": "^11.4.2", "nopt": "^8.1.0", - "normalize-package-data": "^7.0.1", + "normalize-package-data": "^8.0.0", "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.1", - "npm-package-arg": "^12.0.2", - "npm-pick-manifest": "^10.0.0", - "npm-profile": "^11.0.1", - "npm-registry-fetch": "^18.0.2", + "npm-install-checks": "^7.1.2", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-profile": "^12.0.0", + "npm-registry-fetch": "^19.0.0", "npm-user-validate": "^3.0.0", "p-map": "^7.0.3", - "pacote": "^21.0.0", + "pacote": "^21.0.3", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", @@ -12403,10 +12421,10 @@ "semver": "^7.7.2", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", - "supports-color": "^10.0.0", - "tar": "^6.2.1", + "supports-color": "^10.2.2", + "tar": "^7.5.1", "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", + "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^6.0.2", "which": "^5.0.0" @@ -12432,6 +12450,25 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -12449,7 +12486,7 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -12481,7 +12518,7 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.1.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -12511,55 +12548,54 @@ "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { - "version": "3.0.0", + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.4", + "version": "9.1.5", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^4.0.0", "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^3.0.0", "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.1", + "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^4.0.0", "@npmcli/redact": "^3.0.0", - "@npmcli/run-script": "^9.0.1", + "@npmcli/run-script": "^10.0.0", "bin-links": "^5.0.0", - "cacache": "^19.0.1", + "cacache": "^20.0.1", "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", - "lru-cache": "^10.2.2", - "minimatch": "^9.0.4", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", "nopt": "^8.0.0", "npm-install-checks": "^7.1.0", - "npm-package-arg": "^12.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.1", - "pacote": "^21.0.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "proggy": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", - "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", "ssri": "^12.0.0", "treeverse": "^3.0.0", @@ -12573,12 +12609,12 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.0", + "version": "10.4.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/package-json": "^6.0.1", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^5.0.0", "nopt": "^8.1.0", @@ -12602,21 +12638,21 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "6.0.3", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^8.0.0", "ini": "^5.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^10.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { @@ -12635,25 +12671,25 @@ } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "4.0.2", + "version": "5.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0" + "@npmcli/package-json": "^7.0.0", + "glob": "^11.0.3", + "minimatch": "^10.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "cacache": "^19.0.0", + "cacache": "^20.0.0", "json-parse-even-better-errors": "^4.0.0", "pacote": "^21.0.0", "proc-log": "^5.0.0", @@ -12680,24 +12716,24 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "6.2.0", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^8.0.0", + "@npmcli/git": "^7.0.0", + "glob": "^11.0.3", + "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^4.0.0", "proc-log": "^5.0.0", "semver": "^7.5.3", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.2", + "version": "8.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -12727,19 +12763,19 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "9.1.0", + "version": "10.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.0", + "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^8.0.0", "node-gyp": "^11.0.0", "proc-log": "^5.0.0", "which": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@pkgjs/parseargs": { @@ -12752,26 +12788,26 @@ } }, "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.0" + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/core": { - "version": "2.0.0", + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.4.3", + "version": "0.5.0", "inBundle": true, "license": "Apache-2.0", "engines": { @@ -12779,44 +12815,44 @@ } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "make-fetch-happen": "^14.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "3.1.1", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.1", - "tuf-js": "^3.0.1" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/verify": { - "version": "2.1.1", + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.1" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@tufjs/canonical-json": { @@ -12828,7 +12864,7 @@ } }, "node_modules/npm/node_modules/@tufjs/models": { - "version": "3.0.1", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -12836,7 +12872,21 @@ "minimatch": "^9.0.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/abbrev": { @@ -12864,7 +12914,7 @@ } }, "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.1", + "version": "6.2.3", "inBundle": true, "license": "MIT", "engines": { @@ -12924,86 +12974,28 @@ } }, "node_modules/npm/node_modules/cacache": { - "version": "19.0.1", + "version": "20.0.1", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^11.0.3", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", - "tar": "^7.4.3", "unique-filename": "^4.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/chownr": { - "version": "3.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/yallist": { - "version": "5.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/chalk": { - "version": "5.4.1", + "version": "5.6.2", "inBundle": true, "license": "MIT", "engines": { @@ -13014,11 +13006,11 @@ } }, "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", + "version": "3.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/npm/node_modules/ci-info": { @@ -13036,14 +13028,14 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "4.1.3", + "version": "5.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "ip-regex": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/npm/node_modules/cli-columns": { @@ -13100,6 +13092,11 @@ "node": ">= 8" } }, + "node_modules/npm/node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", "inBundle": true, @@ -13126,7 +13123,7 @@ } }, "node_modules/npm/node_modules/debug": { - "version": "4.4.1", + "version": "4.4.3", "inBundle": true, "license": "MIT", "dependencies": { @@ -13142,7 +13139,7 @@ } }, "node_modules/npm/node_modules/diff": { - "version": "7.0.0", + "version": "8.0.2", "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -13221,20 +13218,23 @@ } }, "node_modules/npm/node_modules/glob": { - "version": "10.4.5", + "version": "11.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -13245,14 +13245,14 @@ "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "8.1.0", + "version": "9.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "lru-cache": "^10.0.1" + "lru-cache": "^11.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/http-cache-semantics": { @@ -13297,14 +13297,14 @@ } }, "node_modules/npm/node_modules/ignore-walk": { - "version": "7.0.0", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "minimatch": "^9.0.0" + "minimatch": "^10.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/imurmurhash": { @@ -13324,30 +13324,26 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "8.2.1", + "version": "8.2.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/package-json": "^6.1.0", - "npm-package-arg": "^12.0.0", + "@npmcli/package-json": "^7.0.0", + "npm-package-arg": "^13.0.0", "promzard": "^2.0.0", "read": "^4.0.0", - "semver": "^7.3.5", + "semver": "^7.7.2", "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^6.0.0" + "validate-npm-package-name": "^6.0.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/ip-address": { - "version": "9.0.5", + "version": "10.0.1", "inBundle": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -13364,14 +13360,14 @@ } }, "node_modules/npm/node_modules/is-cidr": { - "version": "5.1.1", + "version": "6.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^4.1.1" + "cidr-regex": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { @@ -13383,29 +13379,27 @@ } }, "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", + "version": "3.1.1", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=16" + } }, "node_modules/npm/node_modules/jackspeak": { - "version": "3.4.3", + "version": "4.1.1", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/npm/node_modules/jsbn": { - "version": "1.1.0", - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "4.0.0", "inBundle": true, @@ -13441,50 +13435,51 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "10.0.1", + "version": "10.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.7", + "version": "8.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", + "@npmcli/arborist": "^9.1.5", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", - "diff": "^7.0.0", - "minimatch": "^9.0.4", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0", - "tar": "^6.2.1" + "diff": "^8.0.2", + "minimatch": "^10.0.3", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", + "tar": "^7.5.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.6", + "version": "10.1.7", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", - "@npmcli/package-json": "^6.1.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/arborist": "^9.1.5", + "@npmcli/package-json": "^7.0.0", + "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", "read": "^4.0.0", - "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", + "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "engines": { @@ -13492,54 +13487,54 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.7", + "version": "7.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4" + "@npmcli/arborist": "^9.1.5" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "8.0.0", + "version": "8.0.1", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.7", + "version": "9.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", - "@npmcli/run-script": "^9.0.1", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0" + "@npmcli/arborist": "^9.1.5", + "@npmcli/run-script": "^10.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.1.0", + "version": "11.1.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/package-json": "^6.2.0", + "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1", + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0", "semver": "^7.3.7", - "sigstore": "^3.0.0", + "sigstore": "^4.0.0", "ssri": "^12.0.0" }, "engines": { @@ -13547,35 +13542,35 @@ } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "9.0.0", + "version": "9.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/git": "^7.0.0", + "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^4.0.0", "proc-log": "^5.0.0", "semver": "^7.3.7" @@ -13585,17 +13580,20 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "10.4.3", + "version": "11.2.1", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "14.0.3", + "version": "15.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", @@ -13607,26 +13605,18 @@ "ssri": "^12.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/minimatch": { - "version": "9.0.5", + "version": "10.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -13667,17 +13657,6 @@ "encoding": "^0.1.13" } }, - "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", "inBundle": true, @@ -13745,37 +13724,14 @@ } }, "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", + "version": "3.1.0", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/npm/node_modules/ms": { @@ -13791,8 +13747,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/npm/node_modules/node-gyp": { - "version": "11.2.0", + "version": "11.4.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -13814,61 +13778,129 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { "version": "3.0.0", "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "minipass": "^7.1.2" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": ">= 18" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "19.0.1", "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", + "node_modules/npm/node_modules/node-gyp/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "14.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" }, "engines": { - "node": ">=18" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { - "version": "5.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/path-scurry": { + "version": "1.11.1", "inBundle": true, "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/nopt": { @@ -13886,16 +13918,16 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "7.0.1", + "version": "8.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-audit-report": { @@ -13918,7 +13950,7 @@ } }, "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.1", + "version": "7.1.2", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -13937,83 +13969,72 @@ } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "12.0.2", + "version": "13.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.0", + "version": "10.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "ignore-walk": "^7.0.0" + "ignore-walk": "^8.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "10.0.0", + "version": "11.0.1", "inBundle": true, "license": "ISC", "dependencies": { "npm-install-checks": "^7.1.0", "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", + "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-profile": { - "version": "11.0.1", + "version": "12.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^18.0.0", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "18.0.2", + "version": "19.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/redact": "^3.0.0", "jsonparse": "^1.3.1", - "make-fetch-happen": "^14.0.0", + "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minizlib": "^3.0.1", - "npm-package-arg": "^12.0.0", + "npm-package-arg": "^13.0.0", "proc-log": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-user-validate": { @@ -14041,27 +14062,27 @@ "license": "BlueOak-1.0.0" }, "node_modules/npm/node_modules/pacote": { - "version": "21.0.0", + "version": "21.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", + "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", + "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^10.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", + "sigstore": "^4.0.0", "ssri": "^12.0.0", - "tar": "^6.1.11" + "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" @@ -14092,15 +14113,15 @@ } }, "node_modules/npm/node_modules/path-scurry": { - "version": "1.11.1", + "version": "2.0.0", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -14199,18 +14220,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", "inBundle": true, @@ -14267,19 +14276,19 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "@sigstore/sign": "^3.1.0", - "@sigstore/tuf": "^3.1.0", - "@sigstore/verify": "^2.1.0" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.0.0", + "@sigstore/tuf": "^4.0.0", + "@sigstore/verify": "^3.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/smart-buffer": { @@ -14292,11 +14301,11 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.8.6", + "version": "2.8.7", "inBundle": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -14350,15 +14359,10 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.21", + "version": "3.0.22", "inBundle": true, "license": "CC0-1.0" }, - "node_modules/npm/node_modules/sprintf-js": { - "version": "1.1.3", - "inBundle": true, - "license": "BSD-3-Clause" - }, "node_modules/npm/node_modules/ssri": { "version": "12.0.0", "inBundle": true, @@ -14421,7 +14425,7 @@ } }, "node_modules/npm/node_modules/supports-color": { - "version": "10.0.0", + "version": "10.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -14432,49 +14436,26 @@ } }, "node_modules/npm/node_modules/tar": { - "version": "6.2.1", + "version": "7.5.1", "inBundle": true, "license": "ISC", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minipass": { + "node_modules/npm/node_modules/tar/node_modules/yallist": { "version": "5.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/npm/node_modules/text-table": { @@ -14483,17 +14464,17 @@ "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", + "version": "2.0.2", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.14", + "version": "0.2.15", "inBundle": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -14503,9 +14484,12 @@ } }, "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", + "version": "6.5.0", "inBundle": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -14535,16 +14519,16 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { - "@tufjs/models": "3.0.1", + "@tufjs/models": "4.0.0", "debug": "^4.4.1", - "make-fetch-happen": "^14.0.3" + "make-fetch-happen": "^15.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/unique-filename": { @@ -14622,14 +14606,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/which/node_modules/isexe": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", "inBundle": true, @@ -14678,7 +14654,7 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -14710,7 +14686,7 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.1.2", "inBundle": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 405157b8..a707b07e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-tooltip": "^1.2.8", - "@react-email/components": "0.5.3", + "@react-email/components": "0.5.5", "@react-email/render": "^1.2.0", "@react-email/tailwind": "1.2.2", "@simplewebauthn/browser": "^13.2.0", @@ -68,9 +68,9 @@ "cookies": "^0.9.1", "cors": "2.8.5", "crypto-js": "^4.2.0", - "drizzle-orm": "0.44.5", + "drizzle-orm": "0.44.6", "eslint": "9.35.0", - "eslint-config-next": "15.5.3", + "eslint-config-next": "15.5.4", "express": "5.1.0", "express-rate-limit": "8.1.0", "glob": "11.0.3", @@ -83,13 +83,13 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.544.0", "moment": "2.30.1", - "next": "15.5.3", + "next": "15.5.4", "next-intl": "^4.3.9", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.6", - "npm": "^11.6.0", + "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", "posthog-node": "^5.8.4", From 68e0911866a4e19e90bd2e1800fed6b451463b4a Mon Sep 17 00:00:00 2001 From: iconoclast hero Date: Fri, 3 Oct 2025 09:25:45 -0400 Subject: [PATCH 105/322] Patch logger for ISO8601 TZ offsets and fix Docker build - server/logger.ts: timestamps now use local TZ offset instead of Z - Dockerfile: replaced 'npm ci --omit=dev' with 'npm install --omit=dev' to fix Alpine build failure - References discussion: https://github.com/orgs/fosrl/discussions/1025 - Note: timestamps default to +00:00 (UTC) unless the user sets environment: TZ= in docker-compose.yaml Optional future improvement: include tzdata in the container for shell/date consistency. --- Dockerfile | 3 ++- server/logger.ts | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 996ef057..b293ae42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,8 @@ RUN apk add --no-cache curl # COPY package.json package-lock.json ./ COPY package*.json ./ -RUN npm ci --omit=dev && npm cache clean --force +#RUN npm ci --omit=dev && npm cache clean --force +RUN npm install --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static diff --git a/server/logger.ts b/server/logger.ts index 15dd6e3f..1c20b3f8 100644 --- a/server/logger.ts +++ b/server/logger.ts @@ -5,6 +5,19 @@ import path from "path"; import { APP_PATH } from "./lib/consts"; import telemetryClient from "./lib/telemetry"; +// helper to get ISO8601 string in the TZ from process.env.TZ +const isoLocal = () => { + const tz = process.env.TZ || "UTC"; + const d = new Date(); + const s = d.toLocaleString("sv-SE", { timeZone: tz, hour12: false }); + const tzOffsetMin = d.getTimezoneOffset(); + const sign = tzOffsetMin <= 0 ? "+" : "-"; + const pad = (n: number) => String(n).padStart(2, "0"); + const hours = pad(Math.floor(Math.abs(tzOffsetMin) / 60)); + const mins = pad(Math.abs(tzOffsetMin) % 60); + return s.replace(" ", "T") + `${sign}${hours}:${mins}`; +}; + const hformat = winston.format.printf( ({ level, label, message, timestamp, stack, ...metadata }) => { let msg = `${timestamp} [${level}]${label ? `[${label}]` : ""}: ${message}`; @@ -29,7 +42,12 @@ if (config.getRawConfig().app.save_logs) { maxSize: "20m", maxFiles: "7d", createSymlink: true, - symlinkName: "pangolin.log" + symlinkName: "pangolin.log", + format: winston.format.combine( + winston.format.timestamp({ format: isoLocal }), + winston.format.splat(), + hformat + ) }) ); transports.push( @@ -42,7 +60,7 @@ if (config.getRawConfig().app.save_logs) { createSymlink: true, symlinkName: ".machinelogs.json", format: winston.format.combine( - winston.format.timestamp(), + winston.format.timestamp({ format: isoLocal }), winston.format.splat(), winston.format.json() ) @@ -56,7 +74,7 @@ const logger = winston.createLogger({ winston.format.errors({ stack: true }), winston.format.colorize(), winston.format.splat(), - winston.format.timestamp(), + winston.format.timestamp({ format: isoLocal }), hformat ), transports From f52605289b566478f429848a793806471e18aa62 Mon Sep 17 00:00:00 2001 From: iconoclast hero Date: Fri, 3 Oct 2025 09:33:43 -0400 Subject: [PATCH 106/322] Patch logger for ISO8601 TZ offsets and fix Docker build - server/logger.ts: timestamps now use local TZ offset instead of Z - Dockerfile: replaced 'npm ci --omit=dev' with 'npm install --omit=dev' to fix Alpine build failure - References discussion: https://github.com/orgs/fosrl/discussions/1025 - Note: timestamps default to +00:00 (UTC) unless the user sets environment: TZ= in docker-compose.yaml Optional future improvement: include tzdata in the container for shell/date consistency. --- Dockerfile | 4 +++- server/logger.ts | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b293ae42..a48e4a1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,9 @@ RUN apk add --no-cache curl # COPY package.json package-lock.json ./ COPY package*.json ./ -#RUN npm ci --omit=dev && npm cache clean --force + +# 'npm ci --omit=dev' with 'npm install --omit=dev' to fix Alpine build failure +# RUN npm ci --omit=dev && npm cache clean --force RUN npm install --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ diff --git a/server/logger.ts b/server/logger.ts index 1c20b3f8..6d3a539b 100644 --- a/server/logger.ts +++ b/server/logger.ts @@ -6,6 +6,7 @@ import { APP_PATH } from "./lib/consts"; import telemetryClient from "./lib/telemetry"; // helper to get ISO8601 string in the TZ from process.env.TZ +// This replaces the default Z (UTC) with the local offset from process.env.TZ const isoLocal = () => { const tz = process.env.TZ || "UTC"; const d = new Date(); @@ -15,6 +16,8 @@ const isoLocal = () => { const pad = (n: number) => String(n).padStart(2, "0"); const hours = pad(Math.floor(Math.abs(tzOffsetMin) / 60)); const mins = pad(Math.abs(tzOffsetMin) % 60); + + // Replace Z in ISO string with local offset return s.replace(" ", "T") + `${sign}${hours}:${mins}`; }; @@ -74,6 +77,8 @@ const logger = winston.createLogger({ winston.format.errors({ stack: true }), winston.format.colorize(), winston.format.splat(), + + // Use isoLocal so timestamps respect TZ env, not just UTC winston.format.timestamp({ format: isoLocal }), hformat ), From 42ef075d4f05c7593b172bd6e88334972be442c8 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Fri, 3 Oct 2025 20:42:58 +0530 Subject: [PATCH 107/322] white background --- src/components/CreateShareLinkForm.tsx | 2 ++ src/components/TwoFactorSetupForm.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/CreateShareLinkForm.tsx b/src/components/CreateShareLinkForm.tsx index 59d4d8c6..b38bab91 100644 --- a/src/components/CreateShareLinkForm.tsx +++ b/src/components/CreateShareLinkForm.tsx @@ -476,7 +476,9 @@ export default function CreateShareLinkForm({

+
+

{t("otpSetupScanQr")}

+
+
From 353e085b0e859c334cf4fd55054f9d84ab3b97fb Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:28 -0700 Subject: [PATCH 108/322] New translations en-us.json (French) --- messages/fr-FR.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index f58db945..3fd3e2df 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", - "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici." -} \ No newline at end of file + "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From ddfda319246551f8c14ab9e746a20cb7d6baee94 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:29 -0700 Subject: [PATCH 109/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 27f7a1a3..1a579ed0 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Редактиране на файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактиране на файл: docker-compose.yml", "emailVerificationRequired": "Потвърждението на Email е необходимо. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", - "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук." -} \ No newline at end of file + "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 7fb35cfebb4f676bb072bbcd59a815f22847301b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:30 -0700 Subject: [PATCH 110/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 645c98be..70bce4dd 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Upravit soubor: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", - "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde." -} \ No newline at end of file + "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From cff3f739dbb12c97d3ea3110fb16aedf51e6b91e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:32 -0700 Subject: [PATCH 111/322] New translations en-us.json (German) --- messages/de-DE.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index e8e75ac0..7eb1e1d3 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", - "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück." -} \ No newline at end of file + "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 4dd672a5902db894d4eeb4b5de48bb52d12aeb5f Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:33 -0700 Subject: [PATCH 112/322] New translations en-us.json (Italian) --- messages/it-IT.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 04ee775c..683e3fad 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", - "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui." -} \ No newline at end of file + "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 7395a64b26c0d3bc3fd75743645813c18fc4ad5b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:34 -0700 Subject: [PATCH 113/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 27457240..30865f1e 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", - "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요." -} \ No newline at end of file + "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From ca8f52d3045ddd289e9bb131df5a94733d836d76 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:35 -0700 Subject: [PATCH 114/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index e1e95e49..ada6f687 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", - "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug." -} \ No newline at end of file + "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From e2ad197d7eefb458af941c84e82f81fc61863b61 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:37 -0700 Subject: [PATCH 115/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 8500739e..cd341974 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", - "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj." -} \ No newline at end of file + "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 7bbbc88c34fdabc088e1bb2cea85307d95551ebd Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:38 -0700 Subject: [PATCH 116/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 0c39b7bc..a6667a9c 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", - "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui." -} \ No newline at end of file + "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 0d8ae0d61589c896ceabb47ade91bcd1e09e6bcb Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:39 -0700 Subject: [PATCH 117/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 07395fcb..2ec7764a 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", - "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда." -} \ No newline at end of file + "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From bbfa6e9c823548cfbab0624f3b8461dd5dab571d Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:40 -0700 Subject: [PATCH 118/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 33ee83c3..f0a66950 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", - "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün." -} \ No newline at end of file + "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 42a7fb949a18ecb2f527b5fa05eb1cfda2b97630 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:42 -0700 Subject: [PATCH 119/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index abaa2c53..1e6ddce8 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", - "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。" -} \ No newline at end of file + "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 0f4ef40600e6d6bd13935302a841c7c1904fe0f8 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:43 -0700 Subject: [PATCH 120/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index a03dc629..fc0cfb0b 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -1522,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", - "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her." -} \ No newline at end of file + "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." +} From 6a18369891ac4d3ac476ecfe2986d8eaea917449 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Fri, 3 Oct 2025 19:07:44 -0700 Subject: [PATCH 121/322] New translations en-us.json (Spanish) --- messages/es-ES.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/messages/es-ES.json b/messages/es-ES.json index 55d046df..0070a03e 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1504,6 +1504,7 @@ "idpGoogleDescription": "Proveedor OAuth2/OIDC de Google", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "customHeaders": "Cabeceras personalizadas", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", "headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.", "domainPickerProvidedDomain": "Dominio proporcionado", "domainPickerFreeProvidedDomain": "Dominio proporcionado gratis", @@ -1521,5 +1522,7 @@ "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", "emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.", - "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí." + "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí.", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From c2c907852d49c1089449d46e09a9e8aa033e1a5a Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 18:36:44 -0700 Subject: [PATCH 122/322] Chungus --- .dockerignore | 1 + .github/workflows/test.yml | 3 + .gitignore | 6 +- Dockerfile | 1 - LICENSE | 31 + bruno/API Keys/Create API Key.bru | 17 + bruno/API Keys/Delete API Key.bru | 11 + bruno/API Keys/List API Key Actions.bru | 11 + bruno/API Keys/List Org API Keys.bru | 11 + bruno/API Keys/List Root API Keys.bru | 11 + bruno/API Keys/Set API Key Actions.bru | 17 + bruno/API Keys/Set API Key Orgs.bru | 17 + bruno/API Keys/folder.bru | 3 + bruno/Auth/login.bru | 4 +- bruno/Auth/logout.bru | 2 +- bruno/IDP/Create OIDC Provider.bru | 22 + bruno/IDP/Generate OIDC URL.bru | 11 + bruno/IDP/folder.bru | 3 + bruno/Internal/Traefik Config.bru | 11 + bruno/Internal/folder.bru | 3 + .../Remote Exit Node/createRemoteExitNode.bru | 11 + bruno/Test.bru | 11 + bruno/bruno.json | 2 +- config/config.yml | 39 + ...r-compose.pg.yml => docker-compose.pgr.yml | 7 + docker-compose.t.yml | 32 - drizzle.pg.config.ts | 13 +- drizzle.sqlite.config.ts | 13 +- messages/cs-CZ.json | 206 +- messages/de-DE.json | 224 +- messages/en-US.json | 216 +- messages/es-ES.json | 216 +- messages/fr-FR.json | 210 +- messages/it-IT.json | 218 +- messages/ko-KR.json | 210 +- messages/nb-NO.json | 212 +- messages/nl-NL.json | 300 +- messages/pl-PL.json | 212 +- messages/pt-PT.json | 482 +- messages/ru-RU.json | 212 +- messages/tr-TR.json | 214 +- messages/zh-CN.json | 216 +- package-lock.json | 7269 +++++++++++++---- package.json | 15 +- server/apiServer.ts | 57 +- server/auth/actions.ts | 14 + server/auth/sessions/app.ts | 23 +- server/auth/sessions/privateRemoteExitNode.ts | 85 + server/auth/sessions/resource.ts | 12 +- server/build.ts | 1 - server/db/countries.ts | 1014 +++ server/db/maxmind.ts | 13 + server/db/pg/driver.ts | 4 +- server/db/pg/index.ts | 3 +- server/db/pg/privateSchema.ts | 245 + server/db/pg/schema.ts | 22 + server/db/private/rateLimit.test.ts | 202 + server/db/private/rateLimit.ts | 458 ++ server/db/private/redis.ts | 782 ++ server/db/private/redisStore.ts | 223 + server/db/queries/verifySessionQueries.ts | 100 +- server/db/sqlite/index.ts | 1 + server/db/sqlite/privateSchema.ts | 239 + server/db/sqlite/schema.ts | 35 +- server/emails/sendEmail.ts | 8 +- .../PrivateNotifyUsageLimitApproaching.tsx | 82 + .../PrivateNotifyUsageLimitReached.tsx | 84 + server/emails/templates/components/Email.tsx | 4 +- server/hybridServer.ts | 2 +- server/index.ts | 5 +- server/integrationApiServer.ts | 14 +- server/lib/blueprints/applyBlueprint.ts | 7 + server/lib/blueprints/proxyResources.ts | 127 +- server/lib/blueprints/types.ts | 17 + server/lib/colorsSchema.ts | 29 + server/lib/config.ts | 133 +- server/lib/consts.ts | 2 + server/lib/encryption.ts | 39 + server/lib/{ => exitNodes}/exitNodeComms.ts | 0 server/lib/exitNodes/exitNodes.ts | 9 +- server/lib/exitNodes/getCurrentExitNodeId.ts | 33 + server/lib/exitNodes/index.ts | 35 +- server/lib/exitNodes/privateExitNodeComms.ts | 145 + server/lib/exitNodes/privateExitNodes.ts | 379 + server/lib/exitNodes/{shared.ts => subnet.ts} | 0 server/lib/geoip.ts | 34 +- server/lib/idp/generateRedirectUrl.ts | 46 +- server/lib/index.ts | 3 - server/lib/private/billing/features.ts | 85 + server/lib/private/billing/index.ts | 16 + server/lib/private/billing/limitSet.ts | 63 + server/lib/private/billing/limitsService.ts | 51 + server/lib/private/billing/tiers.ts | 37 + server/lib/private/billing/usageService.ts | 889 ++ server/lib/private/createUserAccountOrg.ts | 206 + server/lib/private/rateLimitStore.ts | 25 + server/lib/private/readConfigFile.ts | 192 + server/lib/private/resend.ts | 124 + server/lib/private/s3.ts | 19 + server/lib/private/stripe.ts | 28 + server/lib/readConfigFile.ts | 5 +- server/lib/remoteCertificates/certificates.ts | 8 +- server/lib/remoteCertificates/index.ts | 15 +- .../remoteCertificates/privateCertificates.ts | 116 + server/lib/schemas.ts | 9 + .../TraefikConfigManager.ts} | 92 +- .../traefik/getTraefikConfig.ts | 233 +- server/lib/traefik/index.ts | 11 + server/lib/traefik/middleware.ts | 140 + server/lib/traefik/privateGetTraefikConfig.ts | 692 ++ .../lib/{ => traefik}/traefikConfig.test.ts | 2 +- server/lib/validators.ts | 20 + server/middlewares/index.ts | 2 +- .../middlewares/private/corsWithLoginPage.ts | 98 + server/middlewares/private/index.ts | 18 + .../private/verifyCertificateAccess.ts | 126 + server/middlewares/private/verifyIdpAccess.ts | 102 + .../private/verifyLoginPageAccess.ts | 81 + .../private/verifyRemoteExitNode.ts | 56 + .../private/verifyRemoteExitNodeAccess.ts | 118 + server/middlewares/verifyOrgAccess.ts | 2 +- server/routers/auth/changePassword.ts | 2 +- server/routers/auth/checkResourceSession.ts | 2 +- server/routers/auth/disable2fa.ts | 2 +- server/routers/auth/index.ts | 3 + server/routers/auth/initialSetupComplete.ts | 2 +- .../auth/privateGetSessionTransferToken.ts | 97 + server/routers/auth/privateQuickStart.ts | 581 ++ server/routers/auth/privateTransferSession.ts | 128 + .../auth/requestEmailVerificationCode.ts | 2 +- server/routers/auth/requestPasswordReset.ts | 2 +- server/routers/auth/requestTotpSecret.ts | 4 +- server/routers/auth/resetPassword.ts | 2 +- server/routers/auth/securityKey.ts | 2 +- server/routers/auth/setServerAdmin.ts | 2 +- server/routers/auth/signup.ts | 29 + server/routers/auth/verifyEmail.ts | 17 +- server/routers/auth/verifyTotp.ts | 2 +- server/routers/badger/exchangeSession.ts | 2 +- server/routers/badger/verifySession.ts | 59 +- server/routers/client/updateClient.ts | 2 +- server/routers/domain/createOrgDomain.ts | 51 +- server/routers/domain/deleteOrgDomain.ts | 10 + server/routers/domain/index.ts | 2 + ...privateCheckDomainNamespaceAvailability.ts | 127 + .../domain/privateListDomainNamespaces.ts | 130 + server/routers/external.ts | 288 +- server/routers/gerbil/getConfig.ts | 2 +- server/routers/gerbil/getResolvedHostname.ts | 45 +- server/routers/gerbil/peers.ts | 2 +- .../routers/gerbil/privateCreateExitNode.ts | 67 + .../routers/gerbil/privateReceiveBandwidth.ts | 13 + server/routers/gerbil/receiveBandwidth.ts | 103 +- server/routers/gerbil/updateHolePunch.ts | 9 +- server/routers/idp/createOidcIdp.ts | 2 +- server/routers/idp/deleteIdp.ts | 1 + server/routers/idp/generateOidcUrl.ts | 58 +- server/routers/idp/index.ts | 2 +- server/routers/idp/validateOidcCallback.ts | 46 +- server/routers/integration.ts | 10 + server/routers/internal.ts | 20 + server/routers/license/activateLicense.ts | 2 +- server/routers/license/deleteLicenseKey.ts | 2 +- server/routers/license/getLicenseStatus.ts | 2 +- server/routers/license/listLicenseKeys.ts | 2 +- server/routers/license/recheckStatus.ts | 2 +- server/routers/newt/handleGetConfigMessage.ts | 18 +- .../newt/handleNewtPingRequestMessage.ts | 9 +- .../routers/newt/handleNewtRegisterMessage.ts | 160 +- server/routers/newt/targets.ts | 71 +- .../routers/olm/handleOlmRegisterMessage.ts | 10 +- server/routers/org/createOrg.ts | 17 + server/routers/org/getOrg.ts | 22 +- server/routers/org/index.ts | 3 +- .../org/privateSendUsageNotifications.ts | 249 + server/routers/org/updateOrg.ts | 14 +- .../private/billing/createCheckoutSession.ts | 101 + .../routers/private/billing/createCustomer.ts | 48 + .../private/billing/createPortalSession.ts | 89 + .../private/billing/getOrgSubscription.ts | 157 + server/routers/private/billing/getOrgUsage.ts | 129 + .../billing/hooks/handleCustomerCreated.ts | 57 + .../billing/hooks/handleCustomerDeleted.ts | 44 + .../billing/hooks/handleCustomerUpdated.ts | 54 + .../hooks/handleSubscriptionCreated.ts | 153 + .../hooks/handleSubscriptionDeleted.ts | 91 + .../hooks/handleSubscriptionUpdated.ts | 296 + server/routers/private/billing/index.ts | 18 + .../private/billing/internalGetOrgTier.ts | 119 + .../private/billing/subscriptionLifecycle.ts | 45 + server/routers/private/billing/webhooks.ts | 136 + .../private/certificates/createCertificate.ts | 85 + .../private/certificates/getCertificate.ts | 167 + server/routers/private/certificates/index.ts | 15 + .../certificates/restartCertificate.ts | 116 + server/routers/private/hybrid.ts | 1485 ++++ .../private/loginPage/createLoginPage.ts | 225 + .../private/loginPage/deleteLoginPage.ts | 106 + .../routers/private/loginPage/getLoginPage.ts | 86 + server/routers/private/loginPage/index.ts | 19 + .../private/loginPage/loadLoginPage.ts | 148 + .../private/loginPage/updateLoginPage.ts | 227 + .../private/orgIdp/createOrgOidcIdp.ts | 185 + server/routers/private/orgIdp/getOrgIdp.ts | 117 + server/routers/private/orgIdp/index.ts | 17 + server/routers/private/orgIdp/listOrgIdps.ts | 142 + .../private/orgIdp/updateOrgOidcIdp.ts | 237 + .../remoteExitNode/createRemoteExitNode.ts | 278 + .../remoteExitNode/deleteRemoteExitNode.ts | 131 + .../remoteExitNode/getRemoteExitNode.ts | 99 + .../remoteExitNode/getRemoteExitNodeToken.ts | 130 + .../handleRemoteExitNodePingMessage.ts | 140 + .../handleRemoteExitNodeRegisterMessage.ts | 49 + .../routers/private/remoteExitNode/index.ts | 23 + .../remoteExitNode/listRemoteExitNodes.ts | 147 + .../pickRemoteExitNodeDefaults.ts | 71 + .../quickStartRemoteExitNode.ts | 170 + server/routers/resource/createResource.ts | 52 +- server/routers/resource/createResourceRule.ts | 2 +- server/routers/resource/getExchangeToken.ts | 2 +- .../routers/resource/getResourceAuthInfo.ts | 50 +- .../routers/resource/setResourcePassword.ts | 2 +- server/routers/resource/setResourcePincode.ts | 2 +- server/routers/resource/updateResource.ts | 42 +- server/routers/resource/updateResourceRule.ts | 2 +- server/routers/site/createSite.ts | 18 +- server/routers/site/listSites.ts | 8 +- server/routers/site/pickSiteDefaults.ts | 7 + .../routers/supporterKey/hideSupporterKey.ts | 2 +- .../supporterKey/isSupporterKeyVisible.ts | 7 +- .../supporterKey/validateSupporterKey.ts | 2 +- server/routers/target/createTarget.ts | 69 +- server/routers/target/getTarget.ts | 29 +- .../target/handleHealthcheckStatusMessage.ts | 114 + server/routers/target/index.ts | 1 + server/routers/target/listTargets.ts | 45 +- server/routers/target/updateTarget.ts | 63 +- server/routers/traefik/index.ts | 2 +- .../routers/traefik/traefikConfigProvider.ts | 61 + server/routers/user/acceptInvite.ts | 10 + server/routers/user/createOrgUser.ts | 55 + server/routers/user/inviteUser.ts | 38 +- server/routers/user/removeUserOrg.ts | 31 +- server/routers/ws/index.ts | 25 +- server/routers/ws/messageHandlers.ts | 12 +- server/routers/ws/privateWs.ts | 892 ++ server/setup/clearStaleData.ts | 15 +- server/setup/migrationsSqlite.ts | 2 +- src/actions/server.ts | 22 +- src/app/[orgId]/layout.tsx | 30 +- .../settings/(private)/billing/layout.tsx | 97 + .../settings/(private)/billing/page.tsx | 767 ++ .../(private)/idp/[idpId]/general/page.tsx | 996 +++ .../settings/(private)/idp/[idpId]/layout.tsx | 63 + .../settings/(private)/idp/[idpId]/page.tsx | 21 + .../settings/(private)/idp/create/page.tsx | 870 ++ .../[orgId]/settings/(private)/idp/page.tsx | 81 + .../remote-exit-nodes/ExitNodesDataTable.tsx | 55 + .../remote-exit-nodes/ExitNodesTable.tsx | 319 + .../[remoteExitNodeId]/general/page.tsx | 16 + .../[remoteExitNodeId]/layout.tsx | 59 + .../[remoteExitNodeId]/page.tsx | 23 + .../remote-exit-nodes/create/page.tsx | 379 + .../(private)/remote-exit-nodes/page.tsx | 72 + .../settings/access/users/create/page.tsx | 15 +- src/app/[orgId]/settings/general/page.tsx | 89 +- src/app/[orgId]/settings/layout.tsx | 2 +- .../[niceId]/authentication/page.tsx | 31 +- .../resources/[niceId]/proxy/page.tsx | 481 +- .../resources/[niceId]/rules/page.tsx | 166 +- .../settings/resources/create/page.tsx | 28 +- .../[orgId]/settings/sites/create/page.tsx | 168 +- src/app/[orgId]/settings/sites/page.tsx | 2 + src/app/auth/(private)/org/page.tsx | 193 + .../auth/idp/[idpId]/oidc/callback/page.tsx | 31 +- src/app/auth/layout.tsx | 2 +- src/app/auth/login/page.tsx | 20 +- src/app/auth/resource/[resourceGuid]/page.tsx | 81 +- src/app/layout.tsx | 66 +- src/app/navigation.tsx | 28 + src/app/page.tsx | 7 +- src/app/setup/layout.tsx | 2 +- src/components/AutoLoginHandler.tsx | 11 +- src/components/BrandingLogo.tsx | 6 +- src/components/DashboardLoginForm.tsx | 5 +- src/components/DomainPicker.tsx | 136 +- src/components/HealthCheckDialog.tsx | 580 ++ src/components/LayoutHeader.tsx | 25 +- src/components/LayoutSidebar.tsx | 91 +- src/components/LoginForm.tsx | 124 +- src/components/PermissionsSelectBox.tsx | 105 +- src/components/ResourceAuthPortal.tsx | 94 +- src/components/ResourceInfoBox.tsx | 35 +- src/components/SignupForm.tsx | 275 +- src/components/SitesTable.tsx | 31 + src/components/ValidateOidcToken.tsx | 34 +- src/components/private/AuthPageSettings.tsx | 538 ++ .../private/AutoProvisionConfigWidget.tsx | 185 + src/components/private/CertificateStatus.tsx | 156 + src/components/private/IdpLoginButtons.tsx | 135 + src/components/private/OrgIdpDataTable.tsx | 45 + src/components/private/OrgIdpTable.tsx | 219 + src/components/private/RegionSelector.tsx | 101 + src/components/private/SplashImage.tsx | 57 + .../private/ValidateSessionTransferToken.tsx | 84 + src/components/ui/alert.tsx | 4 +- src/components/ui/toaster.tsx | 3 + src/contexts/privateRemoteExitNodeContext.ts | 24 + .../privateSubscriptionStatusContext.ts | 28 + src/hooks/privateUseCertificate.ts | 135 + src/hooks/privateUseRemoteExitNodeContext.ts | 29 + .../privateUseSubscriptionStatusContext.ts | 29 + src/lib/privateThemeColors.ts | 87 + src/lib/pullEnv.ts | 52 +- src/lib/types/env.ts | 36 +- src/lib/types/privateThemeTypes.tsx | 13 + src/middleware.ts | 42 + .../PrivateRemoteExitNodeProvider.tsx | 56 + .../PrivateSubscriptionStatusProvider.tsx | 84 + src/providers/PrivateThemeDataProvider.tsx | 59 + 320 files changed, 35785 insertions(+), 2984 deletions(-) create mode 100644 bruno/API Keys/Create API Key.bru create mode 100644 bruno/API Keys/Delete API Key.bru create mode 100644 bruno/API Keys/List API Key Actions.bru create mode 100644 bruno/API Keys/List Org API Keys.bru create mode 100644 bruno/API Keys/List Root API Keys.bru create mode 100644 bruno/API Keys/Set API Key Actions.bru create mode 100644 bruno/API Keys/Set API Key Orgs.bru create mode 100644 bruno/API Keys/folder.bru create mode 100644 bruno/IDP/Create OIDC Provider.bru create mode 100644 bruno/IDP/Generate OIDC URL.bru create mode 100644 bruno/IDP/folder.bru create mode 100644 bruno/Internal/Traefik Config.bru create mode 100644 bruno/Internal/folder.bru create mode 100644 bruno/Remote Exit Node/createRemoteExitNode.bru create mode 100644 bruno/Test.bru create mode 100644 config/config.yml rename docker-compose.pg.yml => docker-compose.pgr.yml (70%) delete mode 100644 docker-compose.t.yml create mode 100644 server/auth/sessions/privateRemoteExitNode.ts delete mode 100644 server/build.ts create mode 100644 server/db/countries.ts create mode 100644 server/db/maxmind.ts create mode 100644 server/db/pg/privateSchema.ts create mode 100644 server/db/private/rateLimit.test.ts create mode 100644 server/db/private/rateLimit.ts create mode 100644 server/db/private/redis.ts create mode 100644 server/db/private/redisStore.ts create mode 100644 server/db/sqlite/privateSchema.ts create mode 100644 server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx create mode 100644 server/emails/templates/PrivateNotifyUsageLimitReached.tsx create mode 100644 server/lib/colorsSchema.ts create mode 100644 server/lib/encryption.ts rename server/lib/{ => exitNodes}/exitNodeComms.ts (100%) create mode 100644 server/lib/exitNodes/getCurrentExitNodeId.ts create mode 100644 server/lib/exitNodes/privateExitNodeComms.ts create mode 100644 server/lib/exitNodes/privateExitNodes.ts rename server/lib/exitNodes/{shared.ts => subnet.ts} (100%) delete mode 100644 server/lib/index.ts create mode 100644 server/lib/private/billing/features.ts create mode 100644 server/lib/private/billing/index.ts create mode 100644 server/lib/private/billing/limitSet.ts create mode 100644 server/lib/private/billing/limitsService.ts create mode 100644 server/lib/private/billing/tiers.ts create mode 100644 server/lib/private/billing/usageService.ts create mode 100644 server/lib/private/createUserAccountOrg.ts create mode 100644 server/lib/private/rateLimitStore.ts create mode 100644 server/lib/private/readConfigFile.ts create mode 100644 server/lib/private/resend.ts create mode 100644 server/lib/private/s3.ts create mode 100644 server/lib/private/stripe.ts create mode 100644 server/lib/remoteCertificates/privateCertificates.ts rename server/lib/{traefikConfig.ts => traefik/TraefikConfigManager.ts} (93%) rename server/{routers => lib}/traefik/getTraefikConfig.ts (76%) create mode 100644 server/lib/traefik/index.ts create mode 100644 server/lib/traefik/middleware.ts create mode 100644 server/lib/traefik/privateGetTraefikConfig.ts rename server/lib/{ => traefik}/traefikConfig.test.ts (99%) create mode 100644 server/middlewares/private/corsWithLoginPage.ts create mode 100644 server/middlewares/private/index.ts create mode 100644 server/middlewares/private/verifyCertificateAccess.ts create mode 100644 server/middlewares/private/verifyIdpAccess.ts create mode 100644 server/middlewares/private/verifyLoginPageAccess.ts create mode 100644 server/middlewares/private/verifyRemoteExitNode.ts create mode 100644 server/middlewares/private/verifyRemoteExitNodeAccess.ts create mode 100644 server/routers/auth/privateGetSessionTransferToken.ts create mode 100644 server/routers/auth/privateQuickStart.ts create mode 100644 server/routers/auth/privateTransferSession.ts create mode 100644 server/routers/domain/privateCheckDomainNamespaceAvailability.ts create mode 100644 server/routers/domain/privateListDomainNamespaces.ts create mode 100644 server/routers/gerbil/privateCreateExitNode.ts create mode 100644 server/routers/gerbil/privateReceiveBandwidth.ts create mode 100644 server/routers/org/privateSendUsageNotifications.ts create mode 100644 server/routers/private/billing/createCheckoutSession.ts create mode 100644 server/routers/private/billing/createCustomer.ts create mode 100644 server/routers/private/billing/createPortalSession.ts create mode 100644 server/routers/private/billing/getOrgSubscription.ts create mode 100644 server/routers/private/billing/getOrgUsage.ts create mode 100644 server/routers/private/billing/hooks/handleCustomerCreated.ts create mode 100644 server/routers/private/billing/hooks/handleCustomerDeleted.ts create mode 100644 server/routers/private/billing/hooks/handleCustomerUpdated.ts create mode 100644 server/routers/private/billing/hooks/handleSubscriptionCreated.ts create mode 100644 server/routers/private/billing/hooks/handleSubscriptionDeleted.ts create mode 100644 server/routers/private/billing/hooks/handleSubscriptionUpdated.ts create mode 100644 server/routers/private/billing/index.ts create mode 100644 server/routers/private/billing/internalGetOrgTier.ts create mode 100644 server/routers/private/billing/subscriptionLifecycle.ts create mode 100644 server/routers/private/billing/webhooks.ts create mode 100644 server/routers/private/certificates/createCertificate.ts create mode 100644 server/routers/private/certificates/getCertificate.ts create mode 100644 server/routers/private/certificates/index.ts create mode 100644 server/routers/private/certificates/restartCertificate.ts create mode 100644 server/routers/private/hybrid.ts create mode 100644 server/routers/private/loginPage/createLoginPage.ts create mode 100644 server/routers/private/loginPage/deleteLoginPage.ts create mode 100644 server/routers/private/loginPage/getLoginPage.ts create mode 100644 server/routers/private/loginPage/index.ts create mode 100644 server/routers/private/loginPage/loadLoginPage.ts create mode 100644 server/routers/private/loginPage/updateLoginPage.ts create mode 100644 server/routers/private/orgIdp/createOrgOidcIdp.ts create mode 100644 server/routers/private/orgIdp/getOrgIdp.ts create mode 100644 server/routers/private/orgIdp/index.ts create mode 100644 server/routers/private/orgIdp/listOrgIdps.ts create mode 100644 server/routers/private/orgIdp/updateOrgOidcIdp.ts create mode 100644 server/routers/private/remoteExitNode/createRemoteExitNode.ts create mode 100644 server/routers/private/remoteExitNode/deleteRemoteExitNode.ts create mode 100644 server/routers/private/remoteExitNode/getRemoteExitNode.ts create mode 100644 server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts create mode 100644 server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts create mode 100644 server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts create mode 100644 server/routers/private/remoteExitNode/index.ts create mode 100644 server/routers/private/remoteExitNode/listRemoteExitNodes.ts create mode 100644 server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts create mode 100644 server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts create mode 100644 server/routers/target/handleHealthcheckStatusMessage.ts create mode 100644 server/routers/traefik/traefikConfigProvider.ts create mode 100644 server/routers/ws/privateWs.ts create mode 100644 src/app/[orgId]/settings/(private)/billing/layout.tsx create mode 100644 src/app/[orgId]/settings/(private)/billing/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx create mode 100644 src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/idp/create/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/idp/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx create mode 100644 src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx create mode 100644 src/app/auth/(private)/org/page.tsx create mode 100644 src/components/HealthCheckDialog.tsx create mode 100644 src/components/private/AuthPageSettings.tsx create mode 100644 src/components/private/AutoProvisionConfigWidget.tsx create mode 100644 src/components/private/CertificateStatus.tsx create mode 100644 src/components/private/IdpLoginButtons.tsx create mode 100644 src/components/private/OrgIdpDataTable.tsx create mode 100644 src/components/private/OrgIdpTable.tsx create mode 100644 src/components/private/RegionSelector.tsx create mode 100644 src/components/private/SplashImage.tsx create mode 100644 src/components/private/ValidateSessionTransferToken.tsx create mode 100644 src/contexts/privateRemoteExitNodeContext.ts create mode 100644 src/contexts/privateSubscriptionStatusContext.ts create mode 100644 src/hooks/privateUseCertificate.ts create mode 100644 src/hooks/privateUseRemoteExitNodeContext.ts create mode 100644 src/hooks/privateUseSubscriptionStatusContext.ts create mode 100644 src/lib/privateThemeColors.ts create mode 100644 src/lib/types/privateThemeTypes.tsx create mode 100644 src/middleware.ts create mode 100644 src/providers/PrivateRemoteExitNodeProvider.tsx create mode 100644 src/providers/PrivateSubscriptionStatusProvider.tsx create mode 100644 src/providers/PrivateThemeDataProvider.tsx diff --git a/.dockerignore b/.dockerignore index a883e89c..9223d5b5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -28,4 +28,5 @@ LICENSE CONTRIBUTING.md dist .git +migrations/ config/ \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d22c300..61315015 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,9 @@ jobs: - name: Create database index.ts run: echo 'export * from "./sqlite";' > server/db/index.ts + - name: Create build file + run: echo 'export const build = 'oss' as any;' > server/build.ts + - name: Generate database migrations run: npm run db:sqlite:generate diff --git a/.gitignore b/.gitignore index 95b1b9be..6a533ebb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,10 @@ next-env.d.ts *-audit.json migrations tsconfig.tsbuildinfo -config/config.yml +config/config.saas.yml +config/config.oss.yml +config/config.enterprise.yml +config/privateConfig.yml config/postgres config/postgres* config/openapi.yaml @@ -44,3 +47,4 @@ server/build.ts postgres/ dynamic/ certificates/ +*.mmdb diff --git a/Dockerfile b/Dockerfile index 996ef057..00b76f2c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,6 @@ COPY ./cli/wrapper.sh /usr/local/bin/pangctl RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs COPY server/db/names.json ./dist/names.json - COPY public ./public CMD ["npm", "run", "start"] diff --git a/LICENSE b/LICENSE index 0ad25db4..bae61364 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,34 @@ +Copyright (c) 2025 Fossorial, Inc. + +Portions of this software are licensed as follows: + +* All files that include a header specifying they are licensed under the + "Fossorial Commercial License" are governed by the Fossorial Commercial + License terms. The specific terms applicable to each customer depend on the + commercial license tier agreed upon in writing with Fossorial, Inc. + Unauthorized use, copying, modification, or distribution is strictly + prohibited. + +* All files that include a header specifying they are licensed under the GNU + Affero General Public License, Version 3 ("AGPL-3"), are governed by the + AGPL-3 terms. A full copy of the AGPL-3 license is provided below. However, + these files are also available under the Fossorial Commercial License if a + separate commercial license agreement has been executed between the customer + and Fossorial, Inc. + +* All files without a license header are, by default, licensed under the GNU + Affero General Public License, Version 3 (AGPL-3). These files may also be + made available under the Fossorial Commercial License upon agreement with + Fossorial, Inc. + +* All third-party components included in this repository are licensed under + their respective original licenses, as provided by their authors. + +Please consult the header of each individual file to determine the applicable +license. For AGPL-3 licensed files, dual-licensing under the Fossorial +Commercial License is available subject to written agreement with Fossorial, +Inc. + GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 diff --git a/bruno/API Keys/Create API Key.bru b/bruno/API Keys/Create API Key.bru new file mode 100644 index 00000000..009b4b04 --- /dev/null +++ b/bruno/API Keys/Create API Key.bru @@ -0,0 +1,17 @@ +meta { + name: Create API Key + type: http + seq: 1 +} + +put { + url: http://localhost:3000/api/v1/api-key + body: json + auth: inherit +} + +body:json { + { + "isRoot": true + } +} diff --git a/bruno/API Keys/Delete API Key.bru b/bruno/API Keys/Delete API Key.bru new file mode 100644 index 00000000..9285f788 --- /dev/null +++ b/bruno/API Keys/Delete API Key.bru @@ -0,0 +1,11 @@ +meta { + name: Delete API Key + type: http + seq: 2 +} + +delete { + url: http://localhost:3000/api/v1/api-key/dm47aacqxxn3ubj + body: none + auth: inherit +} diff --git a/bruno/API Keys/List API Key Actions.bru b/bruno/API Keys/List API Key Actions.bru new file mode 100644 index 00000000..ae5b721e --- /dev/null +++ b/bruno/API Keys/List API Key Actions.bru @@ -0,0 +1,11 @@ +meta { + name: List API Key Actions + type: http + seq: 6 +} + +get { + url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions + body: none + auth: inherit +} diff --git a/bruno/API Keys/List Org API Keys.bru b/bruno/API Keys/List Org API Keys.bru new file mode 100644 index 00000000..468e964b --- /dev/null +++ b/bruno/API Keys/List Org API Keys.bru @@ -0,0 +1,11 @@ +meta { + name: List Org API Keys + type: http + seq: 4 +} + +get { + url: http://localhost:3000/api/v1/org/home-lab/api-keys + body: none + auth: inherit +} diff --git a/bruno/API Keys/List Root API Keys.bru b/bruno/API Keys/List Root API Keys.bru new file mode 100644 index 00000000..8ef31b68 --- /dev/null +++ b/bruno/API Keys/List Root API Keys.bru @@ -0,0 +1,11 @@ +meta { + name: List Root API Keys + type: http + seq: 3 +} + +get { + url: http://localhost:3000/api/v1/root/api-keys + body: none + auth: inherit +} diff --git a/bruno/API Keys/Set API Key Actions.bru b/bruno/API Keys/Set API Key Actions.bru new file mode 100644 index 00000000..54a35c43 --- /dev/null +++ b/bruno/API Keys/Set API Key Actions.bru @@ -0,0 +1,17 @@ +meta { + name: Set API Key Actions + type: http + seq: 5 +} + +post { + url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions + body: json + auth: inherit +} + +body:json { + { + "actionIds": ["listSites"] + } +} diff --git a/bruno/API Keys/Set API Key Orgs.bru b/bruno/API Keys/Set API Key Orgs.bru new file mode 100644 index 00000000..3f0676c5 --- /dev/null +++ b/bruno/API Keys/Set API Key Orgs.bru @@ -0,0 +1,17 @@ +meta { + name: Set API Key Orgs + type: http + seq: 7 +} + +post { + url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/orgs + body: json + auth: inherit +} + +body:json { + { + "orgIds": ["home-lab"] + } +} diff --git a/bruno/API Keys/folder.bru b/bruno/API Keys/folder.bru new file mode 100644 index 00000000..bb8cd5c7 --- /dev/null +++ b/bruno/API Keys/folder.bru @@ -0,0 +1,3 @@ +meta { + name: API Keys +} diff --git a/bruno/Auth/login.bru b/bruno/Auth/login.bru index 3825a252..2b88066b 100644 --- a/bruno/Auth/login.bru +++ b/bruno/Auth/login.bru @@ -5,14 +5,14 @@ meta { } post { - url: http://localhost:3000/api/v1/auth/login + url: http://localhost:4000/api/v1/auth/login body: json auth: none } body:json { { - "email": "admin@fosrl.io", + "email": "owen@fossorial.io", "password": "Password123!" } } diff --git a/bruno/Auth/logout.bru b/bruno/Auth/logout.bru index 7dd134cc..623cd47f 100644 --- a/bruno/Auth/logout.bru +++ b/bruno/Auth/logout.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://localhost:3000/api/v1/auth/logout + url: http://localhost:4000/api/v1/auth/logout body: none auth: none } diff --git a/bruno/IDP/Create OIDC Provider.bru b/bruno/IDP/Create OIDC Provider.bru new file mode 100644 index 00000000..23e807cf --- /dev/null +++ b/bruno/IDP/Create OIDC Provider.bru @@ -0,0 +1,22 @@ +meta { + name: Create OIDC Provider + type: http + seq: 1 +} + +put { + url: http://localhost:3000/api/v1/org/home-lab/idp/oidc + body: json + auth: inherit +} + +body:json { + { + "clientId": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys", + "clientSecret": "2SlGL2wOGgMEWLI9yUuMAeFxre7qSNJVnXMzyepdNzH1qlxYnC4lKhhQ6a157YQEkYH3vm40KK4RCqbYiF8QIweuPGagPX3oGxEj2exwutoXFfOhtq4hHybQKoFq01Z3", + "authUrl": "http://localhost:9000/application/o/authorize/", + "tokenUrl": "http://localhost:9000/application/o/token/", + "scopes": ["email", "openid", "profile"], + "userIdentifier": "email" + } +} diff --git a/bruno/IDP/Generate OIDC URL.bru b/bruno/IDP/Generate OIDC URL.bru new file mode 100644 index 00000000..90443096 --- /dev/null +++ b/bruno/IDP/Generate OIDC URL.bru @@ -0,0 +1,11 @@ +meta { + name: Generate OIDC URL + type: http + seq: 2 +} + +get { + url: http://localhost:3000/api/v1 + body: none + auth: inherit +} diff --git a/bruno/IDP/folder.bru b/bruno/IDP/folder.bru new file mode 100644 index 00000000..fc136915 --- /dev/null +++ b/bruno/IDP/folder.bru @@ -0,0 +1,3 @@ +meta { + name: IDP +} diff --git a/bruno/Internal/Traefik Config.bru b/bruno/Internal/Traefik Config.bru new file mode 100644 index 00000000..9fc1c1dc --- /dev/null +++ b/bruno/Internal/Traefik Config.bru @@ -0,0 +1,11 @@ +meta { + name: Traefik Config + type: http + seq: 1 +} + +get { + url: http://localhost:3001/api/v1/traefik-config + body: none + auth: inherit +} diff --git a/bruno/Internal/folder.bru b/bruno/Internal/folder.bru new file mode 100644 index 00000000..702931ec --- /dev/null +++ b/bruno/Internal/folder.bru @@ -0,0 +1,3 @@ +meta { + name: Internal +} diff --git a/bruno/Remote Exit Node/createRemoteExitNode.bru b/bruno/Remote Exit Node/createRemoteExitNode.bru new file mode 100644 index 00000000..1c749a31 --- /dev/null +++ b/bruno/Remote Exit Node/createRemoteExitNode.bru @@ -0,0 +1,11 @@ +meta { + name: createRemoteExitNode + type: http + seq: 1 +} + +put { + url: http://localhost:4000/api/v1/org/org_i21aifypnlyxur2/remote-exit-node + body: none + auth: none +} diff --git a/bruno/Test.bru b/bruno/Test.bru new file mode 100644 index 00000000..16286ec8 --- /dev/null +++ b/bruno/Test.bru @@ -0,0 +1,11 @@ +meta { + name: Test + type: http + seq: 2 +} + +get { + url: http://localhost:3000/api/v1 + body: none + auth: inherit +} diff --git a/bruno/bruno.json b/bruno/bruno.json index f19d936a..f0ed66b3 100644 --- a/bruno/bruno.json +++ b/bruno/bruno.json @@ -1,6 +1,6 @@ { "version": "1", - "name": "Pangolin", + "name": "Pangolin Saas", "type": "collection", "ignore": [ "node_modules", diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 00000000..9ae2a1e6 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,39 @@ +app: + dashboard_url: "https://pangolin.internal" + log_level: debug +server: + cors: + origins: + - "https://pangolin.internal" + methods: + - "GET" + - "POST" + - "PUT" + - "DELETE" + - "PATCH" + allowed_headers: + - "X-CSRF-Token" + - "Content-Type" + credentials: true + secret: 1b5f55122e7611f0bf624bafe52c91daadsfjhksd234 +gerbil: + base_endpoint: pangolin.fosrl.io +flags: + require_email_verification: true + disable_signup_without_invite: false + disable_user_create_org: true + allow_base_domain_resources: false + disable_local_sites: false + disable_basic_wireguard_sites: true + enable_integration_api: true + disable_config_managed_domains: true + hide_supporter_key: true + enable_redis: true + enable_clients: true + allow_raw_resources: true +email: + smtp_host: "email-smtp.us-east-1.amazonaws.com" + smtp_port: 587 + smtp_user: "AKIATFBMPNE6PKLOK4MK" + smtp_pass: "BHStM/Nz9B9Crt3YePtsVDnjEp4MZmXqoQvZXWk0MQTC" + no_reply: no-reply@fossorial.io diff --git a/docker-compose.pg.yml b/docker-compose.pgr.yml similarity index 70% rename from docker-compose.pg.yml rename to docker-compose.pgr.yml index ee50d328..2a45f129 100644 --- a/docker-compose.pg.yml +++ b/docker-compose.pgr.yml @@ -11,4 +11,11 @@ services: - ./config/postgres:/var/lib/postgresql/data ports: - "5432:5432" # Map host port 5432 to container port 5432 + restart: no + + redis: + image: redis:latest # Use the latest Redis image + container_name: dev_redis # Name your Redis container + ports: + - "6379:6379" # Map host port 6379 to container port 6379 restart: no diff --git a/docker-compose.t.yml b/docker-compose.t.yml deleted file mode 100644 index 1c7716dd..00000000 --- a/docker-compose.t.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: pangolin -services: - gerbil: - image: gerbil - container_name: gerbil - network_mode: host - restart: unless-stopped - command: - - --reachableAt=http://localhost:3003 - - --generateAndSaveKeyTo=/var/config/key - - --remoteConfig=http://localhost:3001/api/v1/ - - --sni-port=443 - volumes: - - ./config/:/var/config - cap_add: - - NET_ADMIN - - SYS_MODULE - - traefik: - image: docker.io/traefik:v3.4.1 - container_name: traefik - restart: unless-stopped - network_mode: host - command: - - --configFile=/etc/traefik/traefik_config.yml - volumes: - - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration - - ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates - - ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs - - ./certificates:/var/certificates:ro - - ./dynamic:/var/dynamic:ro - diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index 4d1f1e43..2b10d2af 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -1,9 +1,20 @@ import { defineConfig } from "drizzle-kit"; import path from "path"; +import { build } from "@server/build"; + +let schema; +if (build === "oss") { + schema = [path.join("server", "db", "pg", "schema.ts")]; +} else { + schema = [ + path.join("server", "db", "pg", "schema.ts"), + path.join("server", "db", "pg", "privateSchema.ts") + ]; +} export default defineConfig({ dialect: "postgresql", - schema: [path.join("server", "db", "pg", "schema.ts")], + schema: schema, out: path.join("server", "migrations"), verbose: true, dbCredentials: { diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index 94574a89..25bbe7f3 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -1,10 +1,21 @@ +import { build } from "@server/build"; import { APP_PATH } from "@server/lib/consts"; import { defineConfig } from "drizzle-kit"; import path from "path"; +let schema; +if (build === "oss") { + schema = [path.join("server", "db", "sqlite", "schema.ts")]; +} else { + schema = [ + path.join("server", "db", "sqlite", "schema.ts"), + path.join("server", "db", "sqlite", "privateSchema.ts") + ]; +} + export default defineConfig({ dialect: "sqlite", - schema: path.join("server", "db", "sqlite", "schema.ts"), + schema: schema, out: path.join("server", "migrations"), verbose: true, dbCredentials: { diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 70bce4dd..91f4a46a 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -168,6 +168,9 @@ "siteSelect": "Vybrat lokalitu", "siteSearch": "Hledat lokalitu", "siteNotFound": "Nebyla nalezena žádná lokalita.", + "selectCountry": "Vyberte zemi", + "searchCountries": "Hledat země...", + "noCountryFound": "Nebyla nalezena žádná země.", "siteSelectionDescription": "Tato lokalita poskytne připojení k cíli.", "resourceType": "Typ zdroje", "resourceTypeDescription": "Určete, jak chcete přistupovat ke svému zdroji", @@ -1257,6 +1260,48 @@ "domainPickerSubdomain": "Subdoména: {subdomain}", "domainPickerNamespace": "Jmenný prostor: {namespace}", "domainPickerShowMore": "Zobrazit více", + "regionSelectorTitle": "Vybrat region", + "regionSelectorInfo": "Výběr regionu nám pomáhá poskytovat lepší výkon pro vaši polohu. Nemusíte být ve stejném regionu jako váš server.", + "regionSelectorPlaceholder": "Vyberte region", + "regionSelectorComingSoon": "Již brzy", + "billingLoadingSubscription": "Načítání odběru...", + "billingFreeTier": "Volná úroveň", + "billingWarningOverLimit": "Upozornění: Překročili jste jeden nebo více omezení používání. Vaše stránky se nepřipojí dokud nezměníte předplatné nebo neupravíte své používání.", + "billingUsageLimitsOverview": "Přehled omezení použití", + "billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@fossorial.io.", + "billingDataUsage": "Využití dat", + "billingOnlineTime": "Stránka online čas", + "billingUsers": "Aktivní uživatelé", + "billingDomains": "Aktivní domény", + "billingRemoteExitNodes": "Aktivní Samostatně hostované uzly", + "billingNoLimitConfigured": "Žádný limit nenastaven", + "billingEstimatedPeriod": "Odhadované období fakturace", + "billingIncludedUsage": "Zahrnuto využití", + "billingIncludedUsageDescription": "Využití zahrnované s aktuálním plánem předplatného", + "billingFreeTierIncludedUsage": "Povolenky bezplatné úrovně využití", + "billingIncluded": "zahrnuto", + "billingEstimatedTotal": "Odhadovaný celkem:", + "billingNotes": "Poznámky", + "billingEstimateNote": "Toto je odhad založený na aktuálním využití.", + "billingActualChargesMayVary": "Skutečné náklady se mohou lišit.", + "billingBilledAtEnd": "Budete účtováni na konci fakturační doby.", + "billingModifySubscription": "Upravit předplatné", + "billingStartSubscription": "Začít předplatné", + "billingRecurringCharge": "Opakované nabití", + "billingManageSubscriptionSettings": "Spravovat nastavení a nastavení předplatného", + "billingNoActiveSubscription": "Nemáte aktivní předplatné. Začněte předplatné, abyste zvýšili omezení používání.", + "billingFailedToLoadSubscription": "Nepodařilo se načíst odběr", + "billingFailedToLoadUsage": "Nepodařilo se načíst využití", + "billingFailedToGetCheckoutUrl": "Nepodařilo se získat adresu URL pokladny", + "billingPleaseTryAgainLater": "Zkuste to prosím znovu později.", + "billingCheckoutError": "Chyba pokladny", + "billingFailedToGetPortalUrl": "Nepodařilo se získat URL portálu", + "billingPortalError": "Chyba portálu", + "billingDataUsageInfo": "Pokud jste připojeni k cloudu, jsou vám účtována všechna data přenášená prostřednictvím zabezpečených tunelů. To zahrnuje příchozí i odchozí provoz na všech vašich stránkách. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezmenšíte jeho používání. Data nejsou nabírána při používání uzlů.", + "billingOnlineTimeInfo": "Platíte na základě toho, jak dlouho budou vaše stránky připojeny k cloudu. Například, 44,640 minut se rovná jedné stránce 24/7 po celý měsíc. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezkrátíte jeho používání. Čas není vybírán při používání uzlů.", + "billingUsersInfo": "Obdrželi jste platbu za každého uživatele ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních uživatelských účtů ve vašem org.", + "billingDomainInfo": "Platba je účtována za každou doménu ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních doménových účtů na Vašem org.", + "billingRemoteExitNodesInfo": "Za každý spravovaný uzel ve vaší organizaci se vám účtuje denně. Fakturace je počítána na základě počtu aktivních spravovaných uzlů ve vašem org.", "domainNotFound": "Doména nenalezena", "domainNotFoundDescription": "Tento dokument je zakázán, protože doména již neexistuje náš systém. Nastavte prosím novou doménu pro tento dokument.", "failed": "Selhalo", @@ -1320,6 +1365,7 @@ "createDomainDnsPropagationDescription": "Změna DNS může trvat nějakou dobu, než se šíří po internetu. To může trvat kdekoli od několika minut do 48 hodin v závislosti na poskytovateli DNS a nastavení TTL.", "resourcePortRequired": "Pro neHTTP zdroje je vyžadováno číslo portu", "resourcePortNotAllowed": "Číslo portu by nemělo být nastaveno pro HTTP zdroje", + "billingPricingCalculatorLink": "Cenová kalkulačka", "signUpTerms": { "IAgreeToThe": "Souhlasím s", "termsOfService": "podmínky služby", @@ -1368,6 +1414,41 @@ "addNewTarget": "Add New Target", "targetsList": "Seznam cílů", "targetErrorDuplicateTargetFound": "Byl nalezen duplicitní cíl", + "healthCheckHealthy": "Zdravé", + "healthCheckUnhealthy": "Nezdravé", + "healthCheckUnknown": "Neznámý", + "healthCheck": "Kontrola stavu", + "configureHealthCheck": "Konfigurace kontroly stavu", + "configureHealthCheckDescription": "Nastavit sledování zdravotního stavu pro {target}", + "enableHealthChecks": "Povolit kontrolu stavu", + "enableHealthChecksDescription": "Sledujte zdraví tohoto cíle. V případě potřeby můžete sledovat jiný cílový bod, než je cíl.", + "healthScheme": "Způsob", + "healthSelectScheme": "Vybrat metodu", + "healthCheckPath": "Cesta", + "healthHostname": "IP / Hostitel", + "healthPort": "Přístav", + "healthCheckPathDescription": "Cesta ke kontrole zdravotního stavu.", + "healthyIntervalSeconds": "Interval zdraví", + "unhealthyIntervalSeconds": "Nezdravý interval", + "IntervalSeconds": "Interval zdraví", + "timeoutSeconds": "Časový limit", + "timeIsInSeconds": "Čas je v sekundách", + "retryAttempts": "Opakovat pokusy", + "expectedResponseCodes": "Očekávané kódy odezvy", + "expectedResponseCodesDescription": "HTTP kód stavu, který označuje zdravý stav. Ponecháte-li prázdné, 200-300 je považováno za zdravé.", + "customHeaders": "Vlastní záhlaví", + "customHeadersDescription": "Záhlaví oddělená nová řádka: hodnota", + "headersValidationError": "Headers must be in the format: Header-Name: value.", + "saveHealthCheck": "Uložit kontrolu stavu", + "healthCheckSaved": "Kontrola stavu uložena", + "healthCheckSavedDescription": "Nastavení kontroly stavu bylo úspěšně uloženo", + "healthCheckError": "Chyba kontroly stavu", + "healthCheckErrorDescription": "Došlo k chybě při ukládání konfigurace kontroly stavu", + "healthCheckPathRequired": "Je vyžadována cesta kontroly stavu", + "healthCheckMethodRequired": "HTTP metoda je povinná", + "healthCheckIntervalMin": "Interval kontroly musí být nejméně 5 sekund", + "healthCheckTimeoutMin": "Časový limit musí být nejméně 1 sekunda", + "healthCheckRetryMin": "Pokusy opakovat musí být alespoň 1", "httpMethod": "HTTP metoda", "selectHttpMethod": "Vyberte HTTP metodu", "domainPickerSubdomainLabel": "Subdoména", @@ -1381,6 +1462,7 @@ "domainPickerEnterSubdomainToSearch": "Zadejte subdoménu pro hledání a výběr z dostupných domén zdarma.", "domainPickerFreeDomains": "Volné domény", "domainPickerSearchForAvailableDomains": "Hledat dostupné domény", + "domainPickerNotWorkSelfHosted": "Poznámka: Poskytnuté domény nejsou momentálně k dispozici pro vlastní hostované instance.", "resourceDomain": "Doména", "resourceEditDomain": "Upravit doménu", "siteName": "Název webu", @@ -1463,6 +1545,72 @@ "autoLoginError": "Automatická chyba přihlášení", "autoLoginErrorNoRedirectUrl": "Od poskytovatele identity nebyla obdržena žádná adresa URL.", "autoLoginErrorGeneratingUrl": "Nepodařilo se vygenerovat ověřovací URL.", + "remoteExitNodeManageRemoteExitNodes": "Spravovat vlastní hostování", + "remoteExitNodeDescription": "Spravujte uzly pro rozšíření připojení k síti", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Hledat uzly...", + "remoteExitNodeAdd": "Přidat uzel", + "remoteExitNodeErrorDelete": "Chyba při odstraňování uzlu", + "remoteExitNodeQuestionRemove": "Jste si jisti, že chcete odstranit uzel {selectedNode} z organizace?", + "remoteExitNodeMessageRemove": "Po odstranění uzel již nebude přístupný.", + "remoteExitNodeMessageConfirm": "Pro potvrzení zadejte název uzlu níže.", + "remoteExitNodeConfirmDelete": "Potvrdit odstranění uzlu", + "remoteExitNodeDelete": "Odstranit uzel", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Vytvořit uzel", + "description": "Vytvořit nový uzel pro rozšíření síťového připojení", + "viewAllButton": "Zobrazit všechny uzly", + "strategy": { + "title": "Strategie tvorby", + "description": "Vyberte pro manuální konfiguraci vašeho uzlu nebo vygenerujte nové přihlašovací údaje.", + "adopt": { + "title": "Přijmout uzel", + "description": "Zvolte tuto možnost, pokud již máte přihlašovací údaje k uzlu." + }, + "generate": { + "title": "Generovat klíče", + "description": "Vyberte tuto možnost, pokud chcete vygenerovat nové klíče pro uzel" + } + }, + "adopt": { + "title": "Přijmout existující uzel", + "description": "Zadejte přihlašovací údaje existujícího uzlu, který chcete přijmout", + "nodeIdLabel": "ID uzlu", + "nodeIdDescription": "ID existujícího uzlu, který chcete přijmout", + "secretLabel": "Tajný klíč", + "secretDescription": "Tajný klíč existujícího uzlu", + "submitButton": "Přijmout uzel" + }, + "generate": { + "title": "Vygenerovaná pověření", + "description": "Použijte tyto generované přihlašovací údaje pro nastavení vašeho uzlu", + "nodeIdTitle": "ID uzlu", + "secretTitle": "Tajný klíč", + "saveCredentialsTitle": "Přidat přihlašovací údaje do konfigurace", + "saveCredentialsDescription": "Přidejte tyto přihlašovací údaje do vlastního konfiguračního souboru Pangolin uzlu pro dokončení připojení.", + "submitButton": "Vytvořit uzel" + }, + "validation": { + "adoptRequired": "ID uzlu a tajný klíč jsou vyžadovány při přijetí existujícího uzlu" + }, + "errors": { + "loadDefaultsFailed": "Nepodařilo se načíst výchozí hodnoty", + "defaultsNotLoaded": "Výchozí hodnoty nebyly načteny", + "createFailed": "Nepodařilo se vytvořit uzel" + }, + "success": { + "created": "Uzel byl úspěšně vytvořen" + } + }, + "remoteExitNodeSelection": "Výběr uzlu", + "remoteExitNodeSelectionDescription": "Vyberte uzel pro směrování provozu přes tuto lokální stránku", + "remoteExitNodeRequired": "Pro lokální stránky musí být vybrán uzel", + "noRemoteExitNodesAvailable": "Nejsou k dispozici žádné uzly", + "noRemoteExitNodesAvailableDescription": "Pro tuto organizaci nejsou k dispozici žádné uzly. Nejprve vytvořte uzel pro použití lokálních stránek.", + "exitNode": "Ukončit uzel", + "country": "L 343, 22.12.2009, s. 1).", + "rulesMatchCountry": "Aktuálně založené na zdrojové IP adrese", "managedSelfHosted": { "title": "Spravované vlastní hostování", "description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami", @@ -1501,11 +1649,51 @@ }, "internationaldomaindetected": "Zjištěna mezinárodní doména", "willbestoredas": "Bude uloženo jako:", + "roleMappingDescription": "Určete, jak jsou role přiřazeny uživatelům, když se přihlásí, když je povoleno automatické poskytnutí služby.", + "selectRole": "Vyberte roli", + "roleMappingExpression": "Výraz", + "selectRolePlaceholder": "Vyberte roli", + "selectRoleDescription": "Vyberte roli pro přiřazení všem uživatelům od tohoto poskytovatele identity", + "roleMappingExpressionDescription": "Zadejte výraz JMESPath pro získání informací o roli z ID token", + "idpTenantIdRequired": "ID nájemce je povinné", + "invalidValue": "Neplatná hodnota", + "idpTypeLabel": "Typ poskytovatele identity", + "roleMappingExpressionPlaceholder": "např. obsahuje(skupiny, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Konfigurace Google", + "idpGoogleConfigurationDescription": "Konfigurace přihlašovacích údajů Google OAuth2", + "idpGoogleClientIdDescription": "Vaše ID klienta Google OAuth2", + "idpGoogleClientSecretDescription": "Tajný klíč klienta Google OAuth2", + "idpAzureConfiguration": "Nastavení Azure Entra ID", + "idpAzureConfigurationDescription": "Nastavte vaše Azure Entra ID OAuth2", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "vaše-tenant-id", + "idpAzureTenantIdDescription": "Vaše Azure nájemce ID (nalezeno v přehledu Azure Active Directory – Azure)", + "idpAzureClientIdDescription": "Vaše ID registrace aplikace Azure", + "idpAzureClientSecretDescription": "Tajný klíč registrace aplikace Azure", + "idpGoogleTitle": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpGoogleConfigurationTitle": "Konfigurace Google", + "idpAzureConfigurationTitle": "Nastavení Azure Entra ID", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Vaše ID registrace aplikace Azure", + "idpAzureClientSecretDescription2": "Tajný klíč registrace aplikace Azure", + "subnet": "Podsíť", + "subnetDescription": "Podsíť pro konfiguraci sítě této organizace.", + "authPage": "Auth stránka", + "authPageDescription": "Konfigurace autentizační stránky vaší organizace", + "authPageDomain": "Doména ověření stránky", + "noDomainSet": "Není nastavena žádná doména", + "changeDomain": "Změnit doménu", + "selectDomain": "Vybrat doménu", + "restartCertificate": "Restartovat certifikát", + "editAuthPageDomain": "Upravit doménu autentizační stránky", + "setAuthPageDomain": "Nastavit doménu autentické stránky", + "failedToFetchCertificate": "Nepodařilo se načíst certifikát", + "failedToRestartCertificate": "Restartování certifikátu se nezdařilo", + "addDomainToEnableCustomAuthPages": "Přidejte doménu pro povolení vlastních ověřovacích stránek pro vaši organizaci", + "selectDomainForOrgAuthPage": "Vyberte doménu pro ověřovací stránku organizace", "idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Vlastní záhlaví", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Záhlaví musí být ve formátu: název záhlaví: hodnota.", "domainPickerProvidedDomain": "Poskytnutá doména", "domainPickerFreeProvidedDomain": "Zdarma poskytnutá doména", "domainPickerVerified": "Ověřeno", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nemohl být platný pro {domain}.", "domainPickerSubdomainSanitized": "Upravená subdoména", "domainPickerSubdomainCorrected": "\"{sub}\" bylo opraveno na \"{sanitized}\"", + "orgAuthSignInTitle": "Přihlaste se do vaší organizace", + "orgAuthChooseIdpDescription": "Chcete-li pokračovat, vyberte svého poskytovatele identity", + "orgAuthNoIdpConfigured": "Tato organizace nemá nakonfigurovány žádné poskytovatele identity. Místo toho se můžete přihlásit s vaší Pangolinovou identitou.", + "orgAuthSignInWithPangolin": "Přihlásit se pomocí Pangolinu", + "subscriptionRequiredToUse": "Pro použití této funkce je vyžadováno předplatné.", + "idpDisabled": "Poskytovatelé identit jsou zakázáni.", + "orgAuthPageDisabled": "Ověřovací stránka organizace je zakázána.", + "domainRestartedDescription": "Ověření domény bylo úspěšně restartováno", "resourceAddEntrypointsEditFile": "Upravit soubor: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", - "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde." } diff --git a/messages/de-DE.json b/messages/de-DE.json index 7eb1e1d3..f58a0b86 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -168,6 +168,9 @@ "siteSelect": "Standort auswählen", "siteSearch": "Standorte durchsuchen", "siteNotFound": "Keinen Standort gefunden.", + "selectCountry": "Land auswählen", + "searchCountries": "Länder suchen...", + "noCountryFound": "Kein Land gefunden.", "siteSelectionDescription": "Dieser Standort wird die Verbindung zum Ziel herstellen.", "resourceType": "Ressourcentyp", "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Verbunden", "idpErrorConnectingTo": "Es gab ein Problem bei der Verbindung zu {name}. Bitte kontaktieren Sie Ihren Administrator.", "idpErrorNotFound": "IdP nicht gefunden", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Ungültige Einladung", "inviteInvalidDescription": "Der Einladungslink ist ungültig.", "inviteErrorWrongUser": "Einladung ist nicht für diesen Benutzer", @@ -1139,8 +1140,8 @@ "sidebarAllUsers": "Alle Benutzer", "sidebarIdentityProviders": "Identitätsanbieter", "sidebarLicense": "Lizenz", - "sidebarClients": "Clients (Beta)", - "sidebarDomains": "Domains", + "sidebarClients": "Kunden (Beta)", + "sidebarDomains": "Domänen", "enableDockerSocket": "Docker Blaupause aktivieren", "enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.", "enableDockerSocketLink": "Mehr erfahren", @@ -1188,7 +1189,7 @@ "certificateStatus": "Zertifikatsstatus", "loading": "Laden", "restart": "Neustart", - "domains": "Domains", + "domains": "Domänen", "domainsDescription": "Domains für Ihre Organisation verwalten", "domainsSearch": "Domains durchsuchen...", "domainAdd": "Domain hinzufügen", @@ -1201,7 +1202,7 @@ "domainMessageConfirm": "Um zu bestätigen, geben Sie bitte den Domainnamen unten ein.", "domainConfirmDelete": "Domain-Löschung bestätigen", "domainDelete": "Domain löschen", - "domain": "Domain", + "domain": "Domäne", "selectDomainTypeNsName": "Domain-Delegation (NS)", "selectDomainTypeNsDescription": "Diese Domain und alle ihre Subdomains. Verwenden Sie dies, wenn Sie eine gesamte Domainzone kontrollieren möchten.", "selectDomainTypeCnameName": "Einzelne Domain (CNAME)", @@ -1241,7 +1242,7 @@ "sidebarExpand": "Erweitern", "newtUpdateAvailable": "Update verfügbar", "newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.", - "domainPickerEnterDomain": "Domain", + "domainPickerEnterDomain": "Domäne", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Geben Sie die vollständige Domäne der Ressource ein, um verfügbare Optionen zu sehen.", "domainPickerDescriptionSaas": "Geben Sie eine vollständige Domäne, Subdomäne oder einfach einen Namen ein, um verfügbare Optionen zu sehen", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Subdomain: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mehr anzeigen", + "regionSelectorTitle": "Region auswählen", + "regionSelectorInfo": "Das Auswählen einer Region hilft uns, eine bessere Leistung für Ihren Standort bereitzustellen. Sie müssen sich nicht in derselben Region wie Ihr Server befinden.", + "regionSelectorPlaceholder": "Wähle eine Region", + "regionSelectorComingSoon": "Kommt bald", + "billingLoadingSubscription": "Abonnement wird geladen...", + "billingFreeTier": "Kostenlose Stufe", + "billingWarningOverLimit": "Warnung: Sie haben ein oder mehrere Nutzungslimits überschritten. Ihre Webseiten werden nicht verbunden, bis Sie Ihr Abonnement ändern oder Ihren Verbrauch anpassen.", + "billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen", + "billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@fossorial.io.", + "billingDataUsage": "Datenverbrauch", + "billingOnlineTime": "Online-Zeit der Seite", + "billingUsers": "Aktive Benutzer", + "billingDomains": "Aktive Domänen", + "billingRemoteExitNodes": "Aktive selbstgehostete Nodes", + "billingNoLimitConfigured": "Kein Limit konfiguriert", + "billingEstimatedPeriod": "Geschätzter Abrechnungszeitraum", + "billingIncludedUsage": "Inklusive Nutzung", + "billingIncludedUsageDescription": "Nutzung, die in Ihrem aktuellen Abonnementplan enthalten ist", + "billingFreeTierIncludedUsage": "Nutzungskontingente der kostenlosen Stufe", + "billingIncluded": "inbegriffen", + "billingEstimatedTotal": "Geschätzte Gesamtsumme:", + "billingNotes": "Notizen", + "billingEstimateNote": "Dies ist eine Schätzung basierend auf Ihrem aktuellen Verbrauch.", + "billingActualChargesMayVary": "Tatsächliche Kosten können variieren.", + "billingBilledAtEnd": "Sie werden am Ende des Abrechnungszeitraums in Rechnung gestellt.", + "billingModifySubscription": "Abonnement ändern", + "billingStartSubscription": "Abonnement starten", + "billingRecurringCharge": "Wiederkehrende Kosten", + "billingManageSubscriptionSettings": "Verwalten Sie Ihre Abonnement-Einstellungen und Präferenzen", + "billingNoActiveSubscription": "Sie haben kein aktives Abonnement. Starten Sie Ihr Abonnement, um Nutzungslimits zu erhöhen.", + "billingFailedToLoadSubscription": "Fehler beim Laden des Abonnements", + "billingFailedToLoadUsage": "Fehler beim Laden der Nutzung", + "billingFailedToGetCheckoutUrl": "Fehler beim Abrufen der Checkout-URL", + "billingPleaseTryAgainLater": "Bitte versuchen Sie es später noch einmal.", + "billingCheckoutError": "Checkout-Fehler", + "billingFailedToGetPortalUrl": "Fehler beim Abrufen der Portal-URL", + "billingPortalError": "Portalfehler", + "billingDataUsageInfo": "Wenn Sie mit der Cloud verbunden sind, werden alle Daten über Ihre sicheren Tunnel belastet. Dies schließt eingehenden und ausgehenden Datenverkehr über alle Ihre Websites ein. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Daten werden nicht belastet, wenn Sie Knoten verwenden.", + "billingOnlineTimeInfo": "Sie werden belastet, abhängig davon, wie lange Ihre Seiten mit der Cloud verbunden bleiben. Zum Beispiel 44.640 Minuten entspricht einer Site, die 24 Stunden am Tag des Monats läuft. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Die Zeit wird nicht belastet, wenn Sie Knoten verwenden.", + "billingUsersInfo": "Ihnen wird für jeden Benutzer in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Benutzerkonten in Ihrer Organisation.", + "billingDomainInfo": "Ihnen wird für jede Domäne in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Domänenkonten in Ihrer Organisation.", + "billingRemoteExitNodesInfo": "Ihnen wird für jeden verwalteten Node in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven verwalteten Nodes in Ihrer Organisation.", "domainNotFound": "Domain nicht gefunden", "domainNotFoundDescription": "Diese Ressource ist deaktiviert, weil die Domain nicht mehr in unserem System existiert. Bitte setzen Sie eine neue Domain für diese Ressource.", "failed": "Fehlgeschlagen", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Es kann einige Zeit dauern, bis DNS-Änderungen im Internet verbreitet werden. Dies kann je nach Ihrem DNS-Provider und den TTL-Einstellungen von einigen Minuten bis zu 48 Stunden dauern.", "resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich", "resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden", + "billingPricingCalculatorLink": "Preisrechner", "signUpTerms": { "IAgreeToThe": "Ich stimme den", "termsOfService": "Nutzungsbedingungen zu", @@ -1327,7 +1371,7 @@ "privacyPolicy": "Datenschutzrichtlinie" }, "siteRequired": "Standort ist erforderlich.", - "olmTunnel": "Olm Tunnel", + "olmTunnel": "Olm-Tunnel", "olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung", "errorCreatingClient": "Fehler beim Erstellen des Clients", "clientDefaultsNotFound": "Kundenvorgaben nicht gefunden", @@ -1368,6 +1412,41 @@ "addNewTarget": "Neues Ziel hinzufügen", "targetsList": "Ziel-Liste", "targetErrorDuplicateTargetFound": "Doppeltes Ziel gefunden", + "healthCheckHealthy": "Gesund", + "healthCheckUnhealthy": "Ungesund", + "healthCheckUnknown": "Unbekannt", + "healthCheck": "Gesundheits-Check", + "configureHealthCheck": "Gesundheits-Check konfigurieren", + "configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein", + "enableHealthChecks": "Gesundheits-Checks aktivieren", + "enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.", + "healthScheme": "Methode", + "healthSelectScheme": "Methode auswählen", + "healthCheckPath": "Pfad", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "Der Pfad zum Überprüfen des Gesundheitszustands.", + "healthyIntervalSeconds": "Gesunder Intervall", + "unhealthyIntervalSeconds": "Ungesunder Intervall", + "IntervalSeconds": "Gesunder Intervall", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Zeit ist in Sekunden", + "retryAttempts": "Wiederholungsversuche", + "expectedResponseCodes": "Erwartete Antwortcodes", + "expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.", + "customHeaders": "Eigene Kopfzeilen", + "customHeadersDescription": "Header neue Zeile getrennt: Header-Name: Wert", + "headersValidationError": "Header müssen im Format Header-Name: Wert sein.", + "saveHealthCheck": "Gesundheits-Check speichern", + "healthCheckSaved": "Gesundheits-Check gespeichert", + "healthCheckSavedDescription": "Die Konfiguration des Gesundheits-Checks wurde erfolgreich gespeichert", + "healthCheckError": "Fehler beim Gesundheits-Check", + "healthCheckErrorDescription": "Beim Speichern der Gesundheits-Check-Konfiguration ist ein Fehler aufgetreten", + "healthCheckPathRequired": "Gesundheits-Check-Pfad ist erforderlich", + "healthCheckMethodRequired": "HTTP-Methode ist erforderlich", + "healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen", + "healthCheckTimeoutMin": "Timeout muss mindestens 1 Sekunde betragen", + "healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen", "httpMethod": "HTTP-Methode", "selectHttpMethod": "HTTP-Methode auswählen", "domainPickerSubdomainLabel": "Subdomain", @@ -1381,7 +1460,8 @@ "domainPickerEnterSubdomainToSearch": "Geben Sie eine Subdomain ein, um verfügbare freie Domains zu suchen und auszuwählen.", "domainPickerFreeDomains": "Freie Domains", "domainPickerSearchForAvailableDomains": "Verfügbare Domains suchen", - "resourceDomain": "Domain", + "domainPickerNotWorkSelfHosted": "Hinweis: Kostenlose bereitgestellte Domains sind derzeit nicht für selbstgehostete Instanzen verfügbar.", + "resourceDomain": "Domäne", "resourceEditDomain": "Domain bearbeiten", "siteName": "Site-Name", "proxyPort": "Port", @@ -1463,6 +1543,72 @@ "autoLoginError": "Fehler bei der automatischen Anmeldung", "autoLoginErrorNoRedirectUrl": "Keine Weiterleitungs-URL vom Identitätsanbieter erhalten.", "autoLoginErrorGeneratingUrl": "Fehler beim Generieren der Authentifizierungs-URL.", + "remoteExitNodeManageRemoteExitNodes": "Selbst-Hosted verwalten", + "remoteExitNodeDescription": "Knoten verwalten, um die Netzwerkverbindung zu erweitern", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Knoten suchen...", + "remoteExitNodeAdd": "Knoten hinzufügen", + "remoteExitNodeErrorDelete": "Fehler beim Löschen des Knotens", + "remoteExitNodeQuestionRemove": "Sind Sie sicher, dass Sie den Knoten {selectedNode} aus der Organisation entfernen möchten?", + "remoteExitNodeMessageRemove": "Einmal entfernt, wird der Knoten nicht mehr zugänglich sein.", + "remoteExitNodeMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Knotens unten ein.", + "remoteExitNodeConfirmDelete": "Löschknoten bestätigen", + "remoteExitNodeDelete": "Knoten löschen", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Knoten erstellen", + "description": "Erstellen Sie einen neuen Knoten, um Ihre Netzwerkverbindung zu erweitern", + "viewAllButton": "Alle Knoten anzeigen", + "strategy": { + "title": "Erstellungsstrategie", + "description": "Wählen Sie diese Option, um Ihren Knoten manuell zu konfigurieren oder neue Zugangsdaten zu generieren.", + "adopt": { + "title": "Node übernehmen", + "description": "Wählen Sie dies, wenn Sie bereits die Anmeldedaten für den Knoten haben." + }, + "generate": { + "title": "Schlüssel generieren", + "description": "Wählen Sie dies, wenn Sie neue Schlüssel für den Knoten generieren möchten" + } + }, + "adopt": { + "title": "Vorhandenen Node übernehmen", + "description": "Geben Sie die Zugangsdaten des vorhandenen Knotens ein, den Sie übernehmen möchten", + "nodeIdLabel": "Knoten-ID", + "nodeIdDescription": "Die ID des vorhandenen Knotens, den Sie übernehmen möchten", + "secretLabel": "Geheimnis", + "secretDescription": "Der geheime Schlüssel des vorhandenen Knotens", + "submitButton": "Node übernehmen" + }, + "generate": { + "title": "Generierte Anmeldedaten", + "description": "Verwenden Sie diese generierten Anmeldeinformationen, um Ihren Knoten zu konfigurieren", + "nodeIdTitle": "Knoten-ID", + "secretTitle": "Geheimnis", + "saveCredentialsTitle": "Anmeldedaten zur Konfiguration hinzufügen", + "saveCredentialsDescription": "Fügen Sie diese Anmeldedaten zu Ihrer selbst-gehosteten Pangolin Node-Konfigurationsdatei hinzu, um die Verbindung abzuschließen.", + "submitButton": "Knoten erstellen" + }, + "validation": { + "adoptRequired": "Knoten-ID und Geheimnis sind erforderlich, wenn ein existierender Knoten angenommen wird" + }, + "errors": { + "loadDefaultsFailed": "Fehler beim Laden der Standardeinstellungen", + "defaultsNotLoaded": "Standardeinstellungen nicht geladen", + "createFailed": "Knoten konnte nicht erstellt werden" + }, + "success": { + "created": "Knoten erfolgreich erstellt" + } + }, + "remoteExitNodeSelection": "Knotenauswahl", + "remoteExitNodeSelectionDescription": "Wählen Sie einen Knoten aus, durch den Traffic für diese lokale Seite geleitet werden soll", + "remoteExitNodeRequired": "Ein Knoten muss für lokale Seiten ausgewählt sein", + "noRemoteExitNodesAvailable": "Keine Knoten verfügbar", + "noRemoteExitNodesAvailableDescription": "Für diese Organisation sind keine Knoten verfügbar. Erstellen Sie zuerst einen Knoten, um lokale Sites zu verwenden.", + "exitNode": "Exit-Node", + "country": "Land", + "rulesMatchCountry": "Derzeit basierend auf der Quell-IP", "managedSelfHosted": { "title": "Verwaltetes Selbsthosted", "description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Internationale Domain erkannt", "willbestoredas": "Wird gespeichert als:", + "roleMappingDescription": "Legen Sie fest, wie den Benutzern Rollen zugewiesen werden, wenn sie sich anmelden, wenn Auto Provision aktiviert ist.", + "selectRole": "Wählen Sie eine Rolle", + "roleMappingExpression": "Ausdruck", + "selectRolePlaceholder": "Rolle auswählen", + "selectRoleDescription": "Wählen Sie eine Rolle aus, die allen Benutzern von diesem Identitätsprovider zugewiesen werden soll", + "roleMappingExpressionDescription": "Geben Sie einen JMESPath-Ausdruck ein, um Rolleninformationen aus dem ID-Token zu extrahieren", + "idpTenantIdRequired": "Mandant ID ist erforderlich", + "invalidValue": "Ungültiger Wert", + "idpTypeLabel": "Identitätsanbietertyp", + "roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'", + "idpGoogleConfiguration": "Google-Konfiguration", + "idpGoogleConfigurationDescription": "Konfigurieren Sie Ihre Google OAuth2 Zugangsdaten", + "idpGoogleClientIdDescription": "Ihre Google OAuth2 Client-ID", + "idpGoogleClientSecretDescription": "Ihr Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Konfiguration", + "idpAzureConfigurationDescription": "Konfigurieren Sie Ihre Azure Entra ID OAuth2 Zugangsdaten", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "deine Mandant-ID", + "idpAzureTenantIdDescription": "Ihre Azure Mieter-ID (gefunden in Azure Active Directory Übersicht)", + "idpAzureClientIdDescription": "Ihre Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Ihr Azure App Registration Client Secret", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google-Konfiguration", + "idpAzureConfigurationTitle": "Azure Entra ID Konfiguration", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Ihre Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Ihr Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC Provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Eigene Kopfzeilen", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Header müssen im Format Header-Name: Wert sein.", + "subnet": "Subnetz", + "subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.", + "authPage": "Auth Seite", + "authPageDescription": "Konfigurieren Sie die Auth-Seite für Ihre Organisation", + "authPageDomain": "Domain der Auth Seite", + "noDomainSet": "Keine Domäne gesetzt", + "changeDomain": "Domain ändern", + "selectDomain": "Domain auswählen", + "restartCertificate": "Zertifikat neu starten", + "editAuthPageDomain": "Auth Page Domain bearbeiten", + "setAuthPageDomain": "Domain der Auth Seite festlegen", + "failedToFetchCertificate": "Zertifikat konnte nicht abgerufen werden", + "failedToRestartCertificate": "Zertifikat konnte nicht neu gestartet werden", + "addDomainToEnableCustomAuthPages": "Fügen Sie eine Domain hinzu, um benutzerdefinierte Authentifizierungsseiten für Ihre Organisation zu aktivieren", + "selectDomainForOrgAuthPage": "Wählen Sie eine Domain für die Authentifizierungsseite der Organisation", "domainPickerProvidedDomain": "Angegebene Domain", "domainPickerFreeProvidedDomain": "Kostenlose Domain", "domainPickerVerified": "Verifiziert", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" konnte nicht für {domain} gültig gemacht werden.", "domainPickerSubdomainSanitized": "Subdomain bereinigt", "domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"", + "orgAuthSignInTitle": "Bei Ihrer Organisation anmelden", + "orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren", + "orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.", + "orgAuthSignInWithPangolin": "Mit Pangolin anmelden", + "subscriptionRequiredToUse": "Um diese Funktion nutzen zu können, ist ein Abonnement erforderlich.", + "idpDisabled": "Identitätsanbieter sind deaktiviert.", + "orgAuthPageDisabled": "Organisations-Authentifizierungsseite ist deaktiviert.", + "domainRestartedDescription": "Domain-Verifizierung erfolgreich neu gestartet", "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", - "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück." } diff --git a/messages/en-US.json b/messages/en-US.json index f4e063ce..6783e974 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1,3 +1,4 @@ + { "setupCreate": "Create your organization, site, and resources", "setupNewOrg": "New Organization", @@ -94,9 +95,9 @@ "siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.", "siteWg": "Basic WireGuard", "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Local resources only. No tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling. ONLY WORKS ON SELF HOSTED NODES", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "See All Sites", "siteTunnelDescription": "Determine how you want to connect to your site", "siteNewtCredentials": "Newt Credentials", @@ -159,7 +160,7 @@ "resourceHTTP": "HTTPS Resource", "resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.", "resourceRaw": "Raw TCP/UDP Resource", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Create Resource", "resourceCreateDescription": "Follow the steps below to create a new resource", "resourceSeeAll": "See All Resources", @@ -168,6 +169,9 @@ "siteSelect": "Select site", "siteSearch": "Search site", "siteNotFound": "No site found.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "This site will provide connectivity to the target.", "resourceType": "Resource Type", "resourceTypeDescription": "Determine how you want to access your resource", @@ -914,8 +918,6 @@ "idpConnectingToFinished": "Connected", "idpErrorConnectingTo": "There was a problem connecting to {name}. Please contact your administrator.", "idpErrorNotFound": "IdP not found", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Invalid Invite", "inviteInvalidDescription": "The invite link is invalid.", "inviteErrorWrongUser": "Invite is not for this user", @@ -1257,6 +1259,48 @@ "domainPickerSubdomain": "Subdomain: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Show More", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domain Not Found", "domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.", "failed": "Failed", @@ -1320,6 +1364,7 @@ "createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.", "resourcePortRequired": "Port number is required for non-HTTP resources", "resourcePortNotAllowed": "Port number should not be set for HTTP resources", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "I agree to the", "termsOfService": "terms of service", @@ -1368,6 +1413,41 @@ "addNewTarget": "Add New Target", "targetsList": "Targets List", "targetErrorDuplicateTargetFound": "Duplicate target found", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "customHeaders": "Custom Headers", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP Method", "selectHttpMethod": "Select HTTP method", "domainPickerSubdomainLabel": "Subdomain", @@ -1381,6 +1461,7 @@ "domainPickerEnterSubdomainToSearch": "Enter a subdomain to search and select from available free domains.", "domainPickerFreeDomains": "Free Domains", "domainPickerSearchForAvailableDomains": "Search for available domains", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domain", "resourceEditDomain": "Edit Domain", "siteName": "Site Name", @@ -1463,6 +1544,72 @@ "autoLoginError": "Auto Login Error", "autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.", "autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", + "strategy": { + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", + "adopt": { + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." + }, + "generate": { + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" + } + }, + "adopt": { + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" + }, + "generate": { + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" + }, + "validation": { + "adoptRequired": "Node ID and Secret are required when adopting an existing node" + }, + "errors": { + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" + }, + "success": { + "created": "Node created successfully" + } + }, + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Managed Self-Hosted", "description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles", @@ -1501,11 +1648,53 @@ }, "internationaldomaindetected": "International Domain Detected", "willbestoredas": "Will be stored as:", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Custom Headers", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value.", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Provided Domain", "domainPickerFreeProvidedDomain": "Free Provided Domain", "domainPickerVerified": "Verified", @@ -1519,10 +1708,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" could not be made valid for {domain}.", "domainPickerSubdomainSanitized": "Subdomain sanitized", "domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edit file: docker-compose.yml", "emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", "twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } diff --git a/messages/es-ES.json b/messages/es-ES.json index 0070a03e..d48e77b5 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -67,7 +67,7 @@ "siteDocker": "Expandir para detalles de despliegue de Docker", "toggle": "Cambiar", "dockerCompose": "Componer Docker", - "dockerRun": "Docker Run", + "dockerRun": "Ejecutar Docker", "siteLearnLocal": "Los sitios locales no tienen túnel, aprender más", "siteConfirmCopy": "He copiado la configuración", "searchSitesProgress": "Buscar sitios...", @@ -168,6 +168,9 @@ "siteSelect": "Seleccionar sitio", "siteSearch": "Buscar sitio", "siteNotFound": "Sitio no encontrado.", + "selectCountry": "Seleccionar país", + "searchCountries": "Buscar países...", + "noCountryFound": "Ningún país encontrado.", "siteSelectionDescription": "Este sitio proporcionará conectividad al objetivo.", "resourceType": "Tipo de recurso", "resourceTypeDescription": "Determina cómo quieres acceder a tu recurso", @@ -814,7 +817,7 @@ "redirectUrl": "URL de redirección", "redirectUrlAbout": "Acerca de la URL de redirección", "redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.", - "pangolinAuth": "Auth - Pangolin", + "pangolinAuth": "Autenticación - Pangolin", "verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.", "errorOccurred": "Se ha producido un error", "emailErrorVerify": "No se pudo verificar el email:", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Conectado", "idpErrorConnectingTo": "Hubo un problema al conectar con {name}. Por favor, póngase en contacto con su administrador.", "idpErrorNotFound": "IdP no encontrado", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Invitación inválida", "inviteInvalidDescription": "El enlace de invitación no es válido.", "inviteErrorWrongUser": "La invitación no es para este usuario", @@ -1219,7 +1220,7 @@ "billing": "Facturación", "orgBillingDescription": "Gestiona tu información de facturación y suscripciones", "github": "GitHub", - "pangolinHosted": "Pangolin Hosted", + "pangolinHosted": "Pangolin Alojado", "fossorial": "Fossorial", "completeAccountSetup": "Completar configuración de cuenta", "completeAccountSetupDescription": "Establece tu contraseña para comenzar", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Subdominio: {subdomain}", "domainPickerNamespace": "Espacio de nombres: {namespace}", "domainPickerShowMore": "Mostrar más", + "regionSelectorTitle": "Seleccionar Región", + "regionSelectorInfo": "Seleccionar una región nos ayuda a brindar un mejor rendimiento para tu ubicación. No tienes que estar en la misma región que tu servidor.", + "regionSelectorPlaceholder": "Elige una región", + "regionSelectorComingSoon": "Próximamente", + "billingLoadingSubscription": "Cargando suscripción...", + "billingFreeTier": "Nivel Gratis", + "billingWarningOverLimit": "Advertencia: Has excedido uno o más límites de uso. Tus sitios no se conectarán hasta que modifiques tu suscripción o ajustes tu uso.", + "billingUsageLimitsOverview": "Descripción general de los límites de uso", + "billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@fossorial.io.", + "billingDataUsage": "Uso de datos", + "billingOnlineTime": "Tiempo en línea del sitio", + "billingUsers": "Usuarios activos", + "billingDomains": "Dominios activos", + "billingRemoteExitNodes": "Nodos autogestionados activos", + "billingNoLimitConfigured": "No se ha configurado ningún límite", + "billingEstimatedPeriod": "Período de facturación estimado", + "billingIncludedUsage": "Uso incluido", + "billingIncludedUsageDescription": "Uso incluido con su plan de suscripción actual", + "billingFreeTierIncludedUsage": "Permisos de uso del nivel gratuito", + "billingIncluded": "incluido", + "billingEstimatedTotal": "Total Estimado:", + "billingNotes": "Notas", + "billingEstimateNote": "Esta es una estimación basada en tu uso actual.", + "billingActualChargesMayVary": "Los cargos reales pueden variar.", + "billingBilledAtEnd": "Se te facturará al final del período de facturación.", + "billingModifySubscription": "Modificar Suscripción", + "billingStartSubscription": "Iniciar Suscripción", + "billingRecurringCharge": "Cargo Recurrente", + "billingManageSubscriptionSettings": "Administra la configuración y preferencias de tu suscripción", + "billingNoActiveSubscription": "No tienes una suscripción activa. Inicia tu suscripción para aumentar los límites de uso.", + "billingFailedToLoadSubscription": "Error al cargar la suscripción", + "billingFailedToLoadUsage": "Error al cargar el uso", + "billingFailedToGetCheckoutUrl": "Error al obtener la URL de pago", + "billingPleaseTryAgainLater": "Por favor, inténtelo de nuevo más tarde.", + "billingCheckoutError": "Error de pago", + "billingFailedToGetPortalUrl": "Error al obtener la URL del portal", + "billingPortalError": "Error del portal", + "billingDataUsageInfo": "Se le cobran todos los datos transferidos a través de sus túneles seguros cuando se conectan a la nube. Esto incluye tanto tráfico entrante como saliente a través de todos sus sitios. Cuando alcance su límite, sus sitios se desconectarán hasta que actualice su plan o reduzca el uso. Los datos no se cargan cuando se usan nodos.", + "billingOnlineTimeInfo": "Se te cobrará en función del tiempo que tus sitios permanezcan conectados a la nube. Por ejemplo, 44.640 minutos equivale a un sitio que funciona 24/7 durante un mes completo. Cuando alcance su límite, sus sitios se desconectarán hasta que mejore su plan o reduzca el uso. No se cargará el tiempo al usar nodos.", + "billingUsersInfo": "Se te cobra por cada usuario en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de usuario activas en tu organización.", + "billingDomainInfo": "Se te cobra por cada dominio en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de dominio activas en tu organización.", + "billingRemoteExitNodesInfo": "Se te cobra por cada nodo gestionado en tu organización. La facturación se calcula diariamente según la cantidad de nodos gestionados activos en tu organización.", "domainNotFound": "Dominio no encontrado", "domainNotFoundDescription": "Este recurso está deshabilitado porque el dominio ya no existe en nuestro sistema. Por favor, establece un nuevo dominio para este recurso.", "failed": "Fallido", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Los cambios de DNS pueden tardar un tiempo en propagarse a través de internet. Esto puede tardar desde unos pocos minutos hasta 48 horas, dependiendo de tu proveedor de DNS y la configuración de TTL.", "resourcePortRequired": "Se requiere número de puerto para recursos no HTTP", "resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP", + "billingPricingCalculatorLink": "Calculadora de Precios", "signUpTerms": { "IAgreeToThe": "Estoy de acuerdo con los", "termsOfService": "términos del servicio", @@ -1368,6 +1412,41 @@ "addNewTarget": "Agregar nuevo destino", "targetsList": "Lista de destinos", "targetErrorDuplicateTargetFound": "Se encontró un destino duplicado", + "healthCheckHealthy": "Saludable", + "healthCheckUnhealthy": "No saludable", + "healthCheckUnknown": "Desconocido", + "healthCheck": "Chequeo de salud", + "configureHealthCheck": "Configurar Chequeo de Salud", + "configureHealthCheckDescription": "Configura la monitorización de salud para {target}", + "enableHealthChecks": "Activar Chequeos de Salud", + "enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.", + "healthScheme": "Método", + "healthSelectScheme": "Seleccionar método", + "healthCheckPath": "Ruta", + "healthHostname": "IP / Host", + "healthPort": "Puerto", + "healthCheckPathDescription": "La ruta para comprobar el estado de salud.", + "healthyIntervalSeconds": "Intervalo Saludable", + "unhealthyIntervalSeconds": "Intervalo No Saludable", + "IntervalSeconds": "Intervalo Saludable", + "timeoutSeconds": "Tiempo de Espera", + "timeIsInSeconds": "El tiempo está en segundos", + "retryAttempts": "Intentos de Reintento", + "expectedResponseCodes": "Códigos de respuesta esperados", + "expectedResponseCodesDescription": "Código de estado HTTP que indica un estado saludable. Si se deja en blanco, se considera saludable de 200 a 300.", + "customHeaders": "Cabeceras personalizadas", + "customHeadersDescription": "Nueva línea de cabeceras separada: Nombre de cabecera: valor", + "headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.", + "saveHealthCheck": "Guardar Chequeo de Salud", + "healthCheckSaved": "Chequeo de Salud Guardado", + "healthCheckSavedDescription": "La configuración del chequeo de salud se ha guardado correctamente", + "healthCheckError": "Error en el Chequeo de Salud", + "healthCheckErrorDescription": "Ocurrió un error al guardar la configuración del chequeo de salud", + "healthCheckPathRequired": "Se requiere la ruta del chequeo de salud", + "healthCheckMethodRequired": "Se requiere el método HTTP", + "healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos", + "healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo", + "healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Seleccionar método HTTP", "domainPickerSubdomainLabel": "Subdominio", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Ingrese un subdominio para buscar y seleccionar entre dominios gratuitos disponibles.", "domainPickerFreeDomains": "Dominios gratuitos", "domainPickerSearchForAvailableDomains": "Buscar dominios disponibles", + "domainPickerNotWorkSelfHosted": "Nota: Los dominios gratuitos proporcionados no están disponibles para instancias autogestionadas por ahora.", "resourceDomain": "Dominio", "resourceEditDomain": "Editar dominio", "siteName": "Nombre del sitio", @@ -1463,6 +1543,72 @@ "autoLoginError": "Error de inicio de sesión automático", "autoLoginErrorNoRedirectUrl": "No se recibió URL de redirección del proveedor de identidad.", "autoLoginErrorGeneratingUrl": "Error al generar URL de autenticación.", + "remoteExitNodeManageRemoteExitNodes": "Administrar Nodos Autogestionados", + "remoteExitNodeDescription": "Administrar nodos para extender la conectividad de red", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Buscar nodos...", + "remoteExitNodeAdd": "Añadir Nodo", + "remoteExitNodeErrorDelete": "Error al eliminar el nodo", + "remoteExitNodeQuestionRemove": "¿Está seguro de que desea eliminar el nodo {selectedNode} de la organización?", + "remoteExitNodeMessageRemove": "Una vez eliminado, el nodo ya no será accesible.", + "remoteExitNodeMessageConfirm": "Para confirmar, por favor escriba el nombre del nodo a continuación.", + "remoteExitNodeConfirmDelete": "Confirmar eliminar nodo", + "remoteExitNodeDelete": "Eliminar Nodo", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Crear Nodo", + "description": "Crear un nuevo nodo para extender la conectividad de red", + "viewAllButton": "Ver todos los nodos", + "strategy": { + "title": "Estrategia de Creación", + "description": "Elija esto para configurar manualmente su nodo o generar nuevas credenciales.", + "adopt": { + "title": "Adoptar Nodo", + "description": "Elija esto si ya tiene las credenciales para el nodo." + }, + "generate": { + "title": "Generar Claves", + "description": "Elija esto si desea generar nuevas claves para el nodo" + } + }, + "adopt": { + "title": "Adoptar Nodo Existente", + "description": "Introduzca las credenciales del nodo existente que desea adoptar", + "nodeIdLabel": "ID del nodo", + "nodeIdDescription": "El ID del nodo existente que desea adoptar", + "secretLabel": "Secreto", + "secretDescription": "La clave secreta del nodo existente", + "submitButton": "Adoptar Nodo" + }, + "generate": { + "title": "Credenciales Generadas", + "description": "Utilice estas credenciales generadas para configurar su nodo", + "nodeIdTitle": "ID del nodo", + "secretTitle": "Secreto", + "saveCredentialsTitle": "Agregar Credenciales a la Configuración", + "saveCredentialsDescription": "Agrega estas credenciales a tu archivo de configuración del nodo Pangolin autogestionado para completar la conexión.", + "submitButton": "Crear Nodo" + }, + "validation": { + "adoptRequired": "El ID del nodo y el secreto son necesarios al adoptar un nodo existente" + }, + "errors": { + "loadDefaultsFailed": "Falló al cargar los valores predeterminados", + "defaultsNotLoaded": "Valores predeterminados no cargados", + "createFailed": "Error al crear el nodo" + }, + "success": { + "created": "Nodo creado correctamente" + } + }, + "remoteExitNodeSelection": "Selección de nodo", + "remoteExitNodeSelectionDescription": "Seleccione un nodo a través del cual enrutar el tráfico para este sitio local", + "remoteExitNodeRequired": "Un nodo debe ser seleccionado para sitios locales", + "noRemoteExitNodesAvailable": "No hay nodos disponibles", + "noRemoteExitNodesAvailableDescription": "No hay nodos disponibles para esta organización. Crea un nodo primero para usar sitios locales.", + "exitNode": "Nodo de Salida", + "country": "País", + "rulesMatchCountry": "Actualmente basado en IP de origen", "managedSelfHosted": { "title": "Autogestionado", "description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internacional detectado", "willbestoredas": "Se almacenará como:", + "roleMappingDescription": "Determinar cómo se asignan los roles a los usuarios cuando se registran cuando está habilitada la provisión automática.", + "selectRole": "Seleccione un rol", + "roleMappingExpression": "Expresión", + "selectRolePlaceholder": "Elija un rol", + "selectRoleDescription": "Seleccione un rol para asignar a todos los usuarios de este proveedor de identidad", + "roleMappingExpressionDescription": "Introduzca una expresión JMESPath para extraer información de rol del token de ID", + "idpTenantIdRequired": "El ID del cliente es obligatorio", + "invalidValue": "Valor inválido", + "idpTypeLabel": "Tipo de proveedor de identidad", + "roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'", + "idpGoogleConfiguration": "Configuración de Google", + "idpGoogleConfigurationDescription": "Configura tus credenciales de Google OAuth2", + "idpGoogleClientIdDescription": "Tu ID de cliente de Google OAuth2", + "idpGoogleClientSecretDescription": "Tu secreto de cliente de Google OAuth2", + "idpAzureConfiguration": "Configuración de Azure Entra ID", + "idpAzureConfigurationDescription": "Configure sus credenciales de Azure Entra ID OAuth2", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "su-inquilino-id", + "idpAzureTenantIdDescription": "Su ID de inquilino de Azure (encontrado en el resumen de Azure Active Directory)", + "idpAzureClientIdDescription": "Tu ID de Cliente de Registro de Azure App", + "idpAzureClientSecretDescription": "Tu Azure App Registro Cliente secreto", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Configuración de Google", + "idpAzureConfigurationTitle": "Configuración de Azure Entra ID", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Tu ID de Cliente de Registro de Azure App", + "idpAzureClientSecretDescription2": "Tu Azure App Registro Cliente secreto", "idpGoogleDescription": "Proveedor OAuth2/OIDC de Google", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Cabeceras personalizadas", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.", + "subnet": "Subred", + "subnetDescription": "La subred para la configuración de red de esta organización.", + "authPage": "Página Auth", + "authPageDescription": "Configurar la página de autenticación de su organización", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "Ningún dominio establecido", + "changeDomain": "Cambiar dominio", + "selectDomain": "Seleccionar dominio", + "restartCertificate": "Reiniciar certificado", + "editAuthPageDomain": "Editar dominio Auth Page", + "setAuthPageDomain": "Establecer dominio Auth Page", + "failedToFetchCertificate": "Error al obtener el certificado", + "failedToRestartCertificate": "Error al reiniciar el certificado", + "addDomainToEnableCustomAuthPages": "Añadir un dominio para habilitar páginas de autenticación personalizadas para su organización", + "selectDomainForOrgAuthPage": "Seleccione un dominio para la página de autenticación de la organización", "domainPickerProvidedDomain": "Dominio proporcionado", "domainPickerFreeProvidedDomain": "Dominio proporcionado gratis", "domainPickerVerified": "Verificado", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "No se ha podido hacer válido \"{sub}\" para {domain}.", "domainPickerSubdomainSanitized": "Subdominio saneado", "domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"", + "orgAuthSignInTitle": "Inicia sesión en tu organización", + "orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar", + "orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.", + "orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin", + "subscriptionRequiredToUse": "Se requiere una suscripción para utilizar esta función.", + "idpDisabled": "Los proveedores de identidad están deshabilitados.", + "orgAuthPageDisabled": "La página de autenticación de la organización está deshabilitada.", + "domainRestartedDescription": "Verificación de dominio reiniciada con éxito", "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", "emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.", - "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí." } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 3fd3e2df..00da9036 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -168,6 +168,9 @@ "siteSelect": "Sélectionner un site", "siteSearch": "Chercher un site", "siteNotFound": "Aucun site trouvé.", + "selectCountry": "Sélectionnez un pays", + "searchCountries": "Recherchez des pays...", + "noCountryFound": "Aucun pays trouvé.", "siteSelectionDescription": "Ce site fournira la connectivité à la cible.", "resourceType": "Type de ressource", "resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Connecté", "idpErrorConnectingTo": "Un problème est survenu lors de la connexion à {name}. Veuillez contacter votre administrateur.", "idpErrorNotFound": "IdP introuvable", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Invitation invalide", "inviteInvalidDescription": "Le lien d'invitation n'est pas valide.", "inviteErrorWrongUser": "L'invitation n'est pas pour cet utilisateur", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Sous-domaine : {subdomain}", "domainPickerNamespace": "Espace de noms : {namespace}", "domainPickerShowMore": "Afficher plus", + "regionSelectorTitle": "Sélectionner Région", + "regionSelectorInfo": "Sélectionner une région nous aide à offrir de meilleures performances pour votre localisation. Vous n'avez pas besoin d'être dans la même région que votre serveur.", + "regionSelectorPlaceholder": "Choisissez une région", + "regionSelectorComingSoon": "Bientôt disponible", + "billingLoadingSubscription": "Chargement de l'abonnement...", + "billingFreeTier": "Niveau gratuit", + "billingWarningOverLimit": "Attention : Vous avez dépassé une ou plusieurs limites d'utilisation. Vos sites ne se connecteront pas tant que vous n'avez pas modifié votre abonnement ou ajusté votre utilisation.", + "billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation", + "billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@fossorial.io.", + "billingDataUsage": "Utilisation des données", + "billingOnlineTime": "Temps en ligne du site", + "billingUsers": "Utilisateurs actifs", + "billingDomains": "Domaines actifs", + "billingRemoteExitNodes": "Nœuds auto-hébergés actifs", + "billingNoLimitConfigured": "Aucune limite configurée", + "billingEstimatedPeriod": "Période de facturation estimée", + "billingIncludedUsage": "Utilisation incluse", + "billingIncludedUsageDescription": "Utilisation incluse dans votre plan d'abonnement actuel", + "billingFreeTierIncludedUsage": "Tolérances d'utilisation du niveau gratuit", + "billingIncluded": "inclus", + "billingEstimatedTotal": "Total estimé :", + "billingNotes": "Notes", + "billingEstimateNote": "Ceci est une estimation basée sur votre utilisation actuelle.", + "billingActualChargesMayVary": "Les frais réels peuvent varier.", + "billingBilledAtEnd": "Vous serez facturé à la fin de la période de facturation.", + "billingModifySubscription": "Modifier l'abonnement", + "billingStartSubscription": "Démarrer l'abonnement", + "billingRecurringCharge": "Frais récurrents", + "billingManageSubscriptionSettings": "Gérez les paramètres et préférences de votre abonnement", + "billingNoActiveSubscription": "Vous n'avez pas d'abonnement actif. Commencez votre abonnement pour augmenter les limites d'utilisation.", + "billingFailedToLoadSubscription": "Échec du chargement de l'abonnement", + "billingFailedToLoadUsage": "Échec du chargement de l'utilisation", + "billingFailedToGetCheckoutUrl": "Échec pour obtenir l'URL de paiement", + "billingPleaseTryAgainLater": "Veuillez réessayer plus tard.", + "billingCheckoutError": "Erreur de paiement", + "billingFailedToGetPortalUrl": "Échec pour obtenir l'URL du portail", + "billingPortalError": "Erreur du portail", + "billingDataUsageInfo": "Vous êtes facturé pour toutes les données transférées via vos tunnels sécurisés lorsque vous êtes connecté au cloud. Cela inclut le trafic entrant et sortant sur tous vos sites. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre plan ou réduisiez l'utilisation. Les données ne sont pas facturées lors de l'utilisation de nœuds.", + "billingOnlineTimeInfo": "Vous êtes facturé en fonction de la durée de connexion de vos sites au cloud. Par exemple, 44 640 minutes équivaut à un site fonctionnant 24/7 pendant un mois complet. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre forfait ou réduisiez votre consommation. Le temps n'est pas facturé lors de l'utilisation de nœuds.", + "billingUsersInfo": "Vous êtes facturé pour chaque utilisateur dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes utilisateurs actifs dans votre organisation.", + "billingDomainInfo": "Vous êtes facturé pour chaque domaine dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes de domaine actifs dans votre organisation.", + "billingRemoteExitNodesInfo": "Vous êtes facturé pour chaque nœud géré dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.", "domainNotFound": "Domaine introuvable", "domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.", "failed": "Échec", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.", "resourcePortRequired": "Le numéro de port est requis pour les ressources non-HTTP", "resourcePortNotAllowed": "Le numéro de port ne doit pas être défini pour les ressources HTTP", + "billingPricingCalculatorLink": "Calculateur de prix", "signUpTerms": { "IAgreeToThe": "Je suis d'accord avec", "termsOfService": "les conditions d'utilisation", @@ -1368,6 +1412,41 @@ "addNewTarget": "Ajouter une nouvelle cible", "targetsList": "Liste des cibles", "targetErrorDuplicateTargetFound": "Cible en double trouvée", + "healthCheckHealthy": "Sain", + "healthCheckUnhealthy": "En mauvaise santé", + "healthCheckUnknown": "Inconnu", + "healthCheck": "Vérification de l'état de santé", + "configureHealthCheck": "Configurer la vérification de l'état de santé", + "configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}", + "enableHealthChecks": "Activer les vérifications de santé", + "enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.", + "healthScheme": "Méthode", + "healthSelectScheme": "Sélectionnez la méthode", + "healthCheckPath": "Chemin d'accès", + "healthHostname": "IP / Hôte", + "healthPort": "Port", + "healthCheckPathDescription": "Le chemin à vérifier pour le statut de santé.", + "healthyIntervalSeconds": "Intervalle sain", + "unhealthyIntervalSeconds": "Intervalle en mauvaise santé", + "IntervalSeconds": "Intervalle sain", + "timeoutSeconds": "Délai", + "timeIsInSeconds": "Le temps est exprimé en secondes", + "retryAttempts": "Tentatives de réessai", + "expectedResponseCodes": "Codes de réponse attendus", + "expectedResponseCodesDescription": "Code de statut HTTP indiquant un état de santé satisfaisant. Si non renseigné, 200-300 est considéré comme satisfaisant.", + "customHeaders": "En-têtes personnalisés", + "customHeadersDescription": "En-têtes séparés par une nouvelle ligne: En-nom: valeur", + "headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.", + "saveHealthCheck": "Sauvegarder la vérification de l'état de santé", + "healthCheckSaved": "Vérification de l'état de santé enregistrée", + "healthCheckSavedDescription": "La configuration de la vérification de l'état de santé a été enregistrée avec succès", + "healthCheckError": "Erreur de vérification de l'état de santé", + "healthCheckErrorDescription": "Une erreur s'est produite lors de l'enregistrement de la configuration de la vérification de l'état de santé", + "healthCheckPathRequired": "Le chemin de vérification de l'état de santé est requis", + "healthCheckMethodRequired": "La méthode HTTP est requise", + "healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes", + "healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde", + "healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1", "httpMethod": "Méthode HTTP", "selectHttpMethod": "Sélectionnez la méthode HTTP", "domainPickerSubdomainLabel": "Sous-domaine", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Entrez un sous-domaine pour rechercher et sélectionner parmi les domaines gratuits disponibles.", "domainPickerFreeDomains": "Domaines gratuits", "domainPickerSearchForAvailableDomains": "Rechercher des domaines disponibles", + "domainPickerNotWorkSelfHosted": "Remarque : Les domaines fournis gratuitement ne sont pas disponibles pour les instances auto-hébergées pour le moment.", "resourceDomain": "Domaine", "resourceEditDomain": "Modifier le domaine", "siteName": "Nom du site", @@ -1463,6 +1543,72 @@ "autoLoginError": "Erreur de connexion automatique", "autoLoginErrorNoRedirectUrl": "Aucune URL de redirection reçue du fournisseur d'identité.", "autoLoginErrorGeneratingUrl": "Échec de la génération de l'URL d'authentification.", + "remoteExitNodeManageRemoteExitNodes": "Gérer auto-hébergé", + "remoteExitNodeDescription": "Gérer les nœuds pour étendre votre connectivité réseau", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Rechercher des nœuds...", + "remoteExitNodeAdd": "Ajouter un noeud", + "remoteExitNodeErrorDelete": "Erreur lors de la suppression du noeud", + "remoteExitNodeQuestionRemove": "Êtes-vous sûr de vouloir supprimer le noeud {selectedNode} de l'organisation ?", + "remoteExitNodeMessageRemove": "Une fois supprimé, le noeud ne sera plus accessible.", + "remoteExitNodeMessageConfirm": "Pour confirmer, veuillez saisir le nom du noeud ci-dessous.", + "remoteExitNodeConfirmDelete": "Confirmer la suppression du noeud", + "remoteExitNodeDelete": "Supprimer le noeud", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Créer un noeud", + "description": "Créer un nouveau nœud pour étendre votre connectivité réseau", + "viewAllButton": "Voir tous les nœuds", + "strategy": { + "title": "Stratégie de création", + "description": "Choisissez ceci pour configurer manuellement votre nœud ou générer de nouveaux identifiants.", + "adopt": { + "title": "Adopter un nœud", + "description": "Choisissez ceci si vous avez déjà les identifiants pour le noeud." + }, + "generate": { + "title": "Générer des clés", + "description": "Choisissez ceci si vous voulez générer de nouvelles clés pour le noeud" + } + }, + "adopt": { + "title": "Adopter un nœud existant", + "description": "Entrez les identifiants du noeud existant que vous souhaitez adopter", + "nodeIdLabel": "Nœud ID", + "nodeIdDescription": "L'ID du noeud existant que vous voulez adopter", + "secretLabel": "Secret", + "secretDescription": "La clé secrète du noeud existant", + "submitButton": "Noeud d'Adopt" + }, + "generate": { + "title": "Informations d'identification générées", + "description": "Utilisez ces identifiants générés pour configurer votre noeud", + "nodeIdTitle": "Nœud ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Ajouter des identifiants à la config", + "saveCredentialsDescription": "Ajoutez ces informations d'identification à votre fichier de configuration du nœud Pangolin auto-hébergé pour compléter la connexion.", + "submitButton": "Créer un noeud" + }, + "validation": { + "adoptRequired": "ID de nœud et secret sont requis lors de l'adoption d'un noeud existant" + }, + "errors": { + "loadDefaultsFailed": "Échec du chargement des valeurs par défaut", + "defaultsNotLoaded": "Valeurs par défaut non chargées", + "createFailed": "Impossible de créer le noeud" + }, + "success": { + "created": "Noeud créé avec succès" + } + }, + "remoteExitNodeSelection": "Sélection du noeud", + "remoteExitNodeSelectionDescription": "Sélectionnez un nœud pour acheminer le trafic pour ce site local", + "remoteExitNodeRequired": "Un noeud doit être sélectionné pour les sites locaux", + "noRemoteExitNodesAvailable": "Aucun noeud disponible", + "noRemoteExitNodesAvailableDescription": "Aucun noeud n'est disponible pour cette organisation. Créez d'abord un noeud pour utiliser des sites locaux.", + "exitNode": "Nœud de sortie", + "country": "Pays", + "rulesMatchCountry": "Actuellement basé sur l'IP source", "managedSelfHosted": { "title": "Gestion autonome", "description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Domaine international détecté", "willbestoredas": "Sera stocké comme :", + "roleMappingDescription": "Détermine comment les rôles sont assignés aux utilisateurs lorsqu'ils se connectent lorsque la fourniture automatique est activée.", + "selectRole": "Sélectionnez un rôle", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choisir un rôle", + "selectRoleDescription": "Sélectionnez un rôle à assigner à tous les utilisateurs de ce fournisseur d'identité", + "roleMappingExpressionDescription": "Entrez une expression JMESPath pour extraire les informations du rôle du jeton ID", + "idpTenantIdRequired": "L'ID du locataire est requis", + "invalidValue": "Valeur non valide", + "idpTypeLabel": "Type de fournisseur d'identité", + "roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'", + "idpGoogleConfiguration": "Configuration Google", + "idpGoogleConfigurationDescription": "Configurer vos identifiants Google OAuth2", + "idpGoogleClientIdDescription": "Votre identifiant client Google OAuth2", + "idpGoogleClientSecretDescription": "Votre secret client Google OAuth2", + "idpAzureConfiguration": "Configuration de l'entra ID Azure", + "idpAzureConfigurationDescription": "Configurer vos identifiants OAuth2 Azure Entra", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "votre-locataire-id", + "idpAzureTenantIdDescription": "Votre ID de locataire Azure (trouvé dans l'aperçu Azure Active Directory)", + "idpAzureClientIdDescription": "Votre ID client d'enregistrement de l'application Azure", + "idpAzureClientSecretDescription": "Le secret de votre client d'enregistrement Azure App", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Configuration Google", + "idpAzureConfigurationTitle": "Configuration de l'entra ID Azure", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Votre ID client d'enregistrement de l'application Azure", + "idpAzureClientSecretDescription2": "Le secret de votre client d'enregistrement Azure App", "idpGoogleDescription": "Fournisseur Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "En-têtes personnalisés", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.", + "subnet": "Sous-réseau", + "subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.", + "authPage": "Page d'authentification", + "authPageDescription": "Configurer la page d'authentification de votre organisation", + "authPageDomain": "Domaine de la page d'authentification", + "noDomainSet": "Aucun domaine défini", + "changeDomain": "Changer de domaine", + "selectDomain": "Sélectionner un domaine", + "restartCertificate": "Redémarrer le certificat", + "editAuthPageDomain": "Modifier le domaine de la page d'authentification", + "setAuthPageDomain": "Définir le domaine de la page d'authentification", + "failedToFetchCertificate": "Impossible de récupérer le certificat", + "failedToRestartCertificate": "Échec du redémarrage du certificat", + "addDomainToEnableCustomAuthPages": "Ajouter un domaine pour activer les pages d'authentification personnalisées pour votre organisation", + "selectDomainForOrgAuthPage": "Sélectionnez un domaine pour la page d'authentification de l'organisation", "domainPickerProvidedDomain": "Domaine fourni", "domainPickerFreeProvidedDomain": "Domaine fourni gratuitement", "domainPickerVerified": "Vérifié", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "La «{sub}» n'a pas pu être validée pour {domain}.", "domainPickerSubdomainSanitized": "Sous-domaine nettoyé", "domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"", + "orgAuthSignInTitle": "Connectez-vous à votre organisation", + "orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer", + "orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.", + "orgAuthSignInWithPangolin": "Se connecter avec Pangolin", + "subscriptionRequiredToUse": "Un abonnement est requis pour utiliser cette fonctionnalité.", + "idpDisabled": "Les fournisseurs d'identité sont désactivés.", + "orgAuthPageDisabled": "La page d'authentification de l'organisation est désactivée.", + "domainRestartedDescription": "La vérification du domaine a été redémarrée avec succès", "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", - "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici." } diff --git a/messages/it-IT.json b/messages/it-IT.json index 683e3fad..10286f7d 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -36,8 +36,8 @@ "viewSettings": "Visualizza impostazioni", "delete": "Elimina", "name": "Nome", - "online": "Online", - "offline": "Offline", + "online": "In linea", + "offline": "Non in linea", "site": "Sito", "dataIn": "Dati In", "dataOut": "Dati Fuori", @@ -168,6 +168,9 @@ "siteSelect": "Seleziona sito", "siteSearch": "Cerca sito", "siteNotFound": "Nessun sito trovato.", + "selectCountry": "Seleziona paese", + "searchCountries": "Cerca paesi...", + "noCountryFound": "Nessun paese trovato.", "siteSelectionDescription": "Questo sito fornirà connettività all'obiettivo.", "resourceType": "Tipo Di Risorsa", "resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Connesso", "idpErrorConnectingTo": "Si è verificato un problema durante la connessione a {name}. Contatta il tuo amministratore.", "idpErrorNotFound": "IdP non trovato", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Invito Non Valido", "inviteInvalidDescription": "Il link di invito non è valido.", "inviteErrorWrongUser": "L'invito non è per questo utente", @@ -1220,7 +1221,7 @@ "orgBillingDescription": "Gestisci le tue informazioni di fatturazione e abbonamenti", "github": "GitHub", "pangolinHosted": "Pangolin Hosted", - "fossorial": "Fossorial", + "fossorial": "Fossoriale", "completeAccountSetup": "Completa la Configurazione dell'Account", "completeAccountSetupDescription": "Imposta la tua password per iniziare", "accountSetupSent": "Invieremo un codice di configurazione dell'account a questo indirizzo email.", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Sottodominio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostra Altro", + "regionSelectorTitle": "Seleziona regione", + "regionSelectorInfo": "Selezionare una regione ci aiuta a fornire migliori performance per la tua posizione. Non devi necessariamente essere nella stessa regione del tuo server.", + "regionSelectorPlaceholder": "Scegli una regione", + "regionSelectorComingSoon": "Prossimamente", + "billingLoadingSubscription": "Caricamento abbonamento...", + "billingFreeTier": "Piano Gratuito", + "billingWarningOverLimit": "Avviso: Hai superato uno o più limiti di utilizzo. I tuoi siti non si connetteranno finché non modifichi il tuo abbonamento o non adegui il tuo utilizzo.", + "billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo", + "billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@fossorial.io.", + "billingDataUsage": "Utilizzo dei Dati", + "billingOnlineTime": "Tempo Online del Sito", + "billingUsers": "Utenti Attivi", + "billingDomains": "Domini Attivi", + "billingRemoteExitNodes": "Nodi Self-hosted Attivi", + "billingNoLimitConfigured": "Nessun limite configurato", + "billingEstimatedPeriod": "Periodo di Fatturazione Stimato", + "billingIncludedUsage": "Utilizzo Incluso", + "billingIncludedUsageDescription": "Utilizzo incluso nel tuo piano di abbonamento corrente", + "billingFreeTierIncludedUsage": "Elenchi di utilizzi inclusi nel piano gratuito", + "billingIncluded": "incluso", + "billingEstimatedTotal": "Totale Stimato:", + "billingNotes": "Note", + "billingEstimateNote": "Questa è una stima basata sul tuo utilizzo attuale.", + "billingActualChargesMayVary": "I costi effettivi possono variare.", + "billingBilledAtEnd": "Sarai fatturato alla fine del periodo di fatturazione.", + "billingModifySubscription": "Modifica Abbonamento", + "billingStartSubscription": "Inizia Abbonamento", + "billingRecurringCharge": "Addebito Ricorrente", + "billingManageSubscriptionSettings": "Gestisci impostazioni e preferenze dell'abbonamento", + "billingNoActiveSubscription": "Non hai un abbonamento attivo. Avvia il tuo abbonamento per aumentare i limiti di utilizzo.", + "billingFailedToLoadSubscription": "Caricamento abbonamento fallito", + "billingFailedToLoadUsage": "Caricamento utilizzo fallito", + "billingFailedToGetCheckoutUrl": "Errore durante l'ottenimento dell'URL di pagamento", + "billingPleaseTryAgainLater": "Per favore, riprova più tardi.", + "billingCheckoutError": "Errore di Pagamento", + "billingFailedToGetPortalUrl": "Errore durante l'ottenimento dell'URL del portale", + "billingPortalError": "Errore del Portale", + "billingDataUsageInfo": "Hai addebitato tutti i dati trasferiti attraverso i tunnel sicuri quando sei connesso al cloud. Questo include sia il traffico in entrata e in uscita attraverso tutti i siti. Quando si raggiunge il limite, i siti si disconnetteranno fino a quando non si aggiorna il piano o si riduce l'utilizzo. I dati non vengono caricati quando si utilizzano nodi.", + "billingOnlineTimeInfo": "Ti viene addebitato in base al tempo in cui i tuoi siti rimangono connessi al cloud. Ad esempio, 44,640 minuti è uguale a un sito in esecuzione 24/7 per un mese intero. Quando raggiungi il tuo limite, i tuoi siti si disconnetteranno fino a quando non aggiorni il tuo piano o riduci l'utilizzo. Il tempo non viene caricato quando si usano i nodi.", + "billingUsersInfo": "Sei addebitato per ogni utente nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account utente attivi nella tua organizzazione.", + "billingDomainInfo": "Sei addebitato per ogni dominio nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account dominio attivi nella tua organizzazione.", + "billingRemoteExitNodesInfo": "Sei addebitato per ogni nodo gestito nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di nodi gestiti attivi nella tua organizzazione.", "domainNotFound": "Domini Non Trovati", "domainNotFoundDescription": "Questa risorsa è disabilitata perché il dominio non esiste più nel nostro sistema. Si prega di impostare un nuovo dominio per questa risorsa.", "failed": "Fallito", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Le modifiche DNS possono richiedere del tempo per propagarsi in Internet. Questo può richiedere da pochi minuti a 48 ore, a seconda del tuo provider DNS e delle impostazioni TTL.", "resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP", "resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP", + "billingPricingCalculatorLink": "Calcolatore di Prezzi", "signUpTerms": { "IAgreeToThe": "Accetto i", "termsOfService": "termini di servizio", @@ -1327,7 +1371,7 @@ "privacyPolicy": "informativa sulla privacy" }, "siteRequired": "Il sito è richiesto.", - "olmTunnel": "Olm Tunnel", + "olmTunnel": "Tunnel Olm", "olmTunnelDescription": "Usa Olm per la connettività client", "errorCreatingClient": "Errore nella creazione del client", "clientDefaultsNotFound": "Impostazioni predefinite del client non trovate", @@ -1368,6 +1412,41 @@ "addNewTarget": "Aggiungi Nuovo Target", "targetsList": "Elenco dei Target", "targetErrorDuplicateTargetFound": "Target duplicato trovato", + "healthCheckHealthy": "Sano", + "healthCheckUnhealthy": "Non Sano", + "healthCheckUnknown": "Sconosciuto", + "healthCheck": "Controllo Salute", + "configureHealthCheck": "Configura Controllo Salute", + "configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}", + "enableHealthChecks": "Abilita i Controlli di Salute", + "enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.", + "healthScheme": "Metodo", + "healthSelectScheme": "Seleziona Metodo", + "healthCheckPath": "Percorso", + "healthHostname": "IP / Host", + "healthPort": "Porta", + "healthCheckPathDescription": "Percorso per verificare lo stato di salute.", + "healthyIntervalSeconds": "Intervallo Sano", + "unhealthyIntervalSeconds": "Intervallo Non Sano", + "IntervalSeconds": "Intervallo Sano", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Il tempo è in secondi", + "retryAttempts": "Tentativi di Riprova", + "expectedResponseCodes": "Codici di Risposta Attesi", + "expectedResponseCodesDescription": "Codice di stato HTTP che indica lo stato di salute. Se lasciato vuoto, considerato sano è compreso tra 200-300.", + "customHeaders": "Intestazioni Personalizzate", + "customHeadersDescription": "Intestazioni nuova riga separate: Intestazione-Nome: valore", + "headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.", + "saveHealthCheck": "Salva Controllo Salute", + "healthCheckSaved": "Controllo Salute Salvato", + "healthCheckSavedDescription": "La configurazione del controllo salute è stata salvata con successo", + "healthCheckError": "Errore Controllo Salute", + "healthCheckErrorDescription": "Si è verificato un errore durante il salvataggio della configurazione del controllo salute.", + "healthCheckPathRequired": "Il percorso del controllo salute è richiesto", + "healthCheckMethodRequired": "Metodo HTTP richiesto", + "healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi", + "healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo", + "healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1", "httpMethod": "Metodo HTTP", "selectHttpMethod": "Seleziona metodo HTTP", "domainPickerSubdomainLabel": "Sottodominio", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Inserisci un sottodominio per cercare e selezionare dai domini gratuiti disponibili.", "domainPickerFreeDomains": "Domini Gratuiti", "domainPickerSearchForAvailableDomains": "Cerca domini disponibili", + "domainPickerNotWorkSelfHosted": "Nota: I domini forniti gratuitamente non sono disponibili per le istanze self-hosted al momento.", "resourceDomain": "Dominio", "resourceEditDomain": "Modifica Dominio", "siteName": "Nome del Sito", @@ -1463,6 +1543,72 @@ "autoLoginError": "Errore di Accesso Automatico", "autoLoginErrorNoRedirectUrl": "Nessun URL di reindirizzamento ricevuto dal provider di identità.", "autoLoginErrorGeneratingUrl": "Impossibile generare l'URL di autenticazione.", + "remoteExitNodeManageRemoteExitNodes": "Gestisci Self-Hosted", + "remoteExitNodeDescription": "Gestisci i nodi per estendere la connettività di rete", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Cerca nodi...", + "remoteExitNodeAdd": "Aggiungi Nodo", + "remoteExitNodeErrorDelete": "Errore nell'eliminare il nodo", + "remoteExitNodeQuestionRemove": "Sei sicuro di voler rimuovere il nodo {selectedNode} dall'organizzazione?", + "remoteExitNodeMessageRemove": "Una volta rimosso, il nodo non sarà più accessibile.", + "remoteExitNodeMessageConfirm": "Per confermare, digita il nome del nodo qui sotto.", + "remoteExitNodeConfirmDelete": "Conferma Eliminazione Nodo", + "remoteExitNodeDelete": "Elimina Nodo", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Crea Nodo", + "description": "Crea un nuovo nodo per estendere la connettività di rete", + "viewAllButton": "Visualizza Tutti I Nodi", + "strategy": { + "title": "Strategia di Creazione", + "description": "Scegli questa opzione per configurare manualmente il nodo o generare nuove credenziali.", + "adopt": { + "title": "Adotta Nodo", + "description": "Scegli questo se hai già le credenziali per il nodo." + }, + "generate": { + "title": "Genera Chiavi", + "description": "Scegli questa opzione se vuoi generare nuove chiavi per il nodo" + } + }, + "adopt": { + "title": "Adotta Nodo Esistente", + "description": "Inserisci le credenziali del nodo esistente che vuoi adottare", + "nodeIdLabel": "ID Nodo", + "nodeIdDescription": "L'ID del nodo esistente che si desidera adottare", + "secretLabel": "Segreto", + "secretDescription": "La chiave segreta del nodo esistente", + "submitButton": "Adotta Nodo" + }, + "generate": { + "title": "Credenziali Generate", + "description": "Usa queste credenziali generate per configurare il nodo", + "nodeIdTitle": "ID Nodo", + "secretTitle": "Segreto", + "saveCredentialsTitle": "Aggiungi Credenziali alla Configurazione", + "saveCredentialsDescription": "Aggiungi queste credenziali al tuo file di configurazione del nodo self-hosted Pangolin per completare la connessione.", + "submitButton": "Crea Nodo" + }, + "validation": { + "adoptRequired": "L'ID del nodo e il segreto sono necessari quando si adotta un nodo esistente" + }, + "errors": { + "loadDefaultsFailed": "Caricamento impostazioni predefinite fallito", + "defaultsNotLoaded": "Impostazioni predefinite non caricate", + "createFailed": "Impossibile creare il nodo" + }, + "success": { + "created": "Nodo creato con successo" + } + }, + "remoteExitNodeSelection": "Selezione Nodo", + "remoteExitNodeSelectionDescription": "Seleziona un nodo per instradare il traffico per questo sito locale", + "remoteExitNodeRequired": "Un nodo deve essere selezionato per i siti locali", + "noRemoteExitNodesAvailable": "Nessun Nodo Disponibile", + "noRemoteExitNodesAvailableDescription": "Non ci sono nodi disponibili per questa organizzazione. Crea un nodo prima per usare i siti locali.", + "exitNode": "Nodo di Uscita", + "country": "Paese", + "rulesMatchCountry": "Attualmente basato sull'IP di origine", "managedSelfHosted": { "title": "Gestito Auto-Ospitato", "description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internazionale Rilevato", "willbestoredas": "Verrà conservato come:", + "roleMappingDescription": "Determinare come i ruoli sono assegnati agli utenti quando accedono quando è abilitata la fornitura automatica.", + "selectRole": "Seleziona un ruolo", + "roleMappingExpression": "Espressione", + "selectRolePlaceholder": "Scegli un ruolo", + "selectRoleDescription": "Seleziona un ruolo da assegnare a tutti gli utenti da questo provider di identità", + "roleMappingExpressionDescription": "Inserire un'espressione JMESPath per estrarre le informazioni sul ruolo dal token ID", + "idpTenantIdRequired": "L'ID dell'inquilino è obbligatorio", + "invalidValue": "Valore non valido", + "idpTypeLabel": "Tipo Provider Identità", + "roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' 'Membro'", + "idpGoogleConfiguration": "Configurazione Google", + "idpGoogleConfigurationDescription": "Configura le tue credenziali di Google OAuth2", + "idpGoogleClientIdDescription": "Il Tuo Client Id Google OAuth2", + "idpGoogleClientSecretDescription": "Il Tuo Client Google OAuth2 Secret", + "idpAzureConfiguration": "Configurazione Azure Entra ID", + "idpAzureConfigurationDescription": "Configura le credenziali OAuth2 di Azure Entra ID", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "iltuo-inquilino-id", + "idpAzureTenantIdDescription": "Il tuo ID del tenant Azure (trovato nella panoramica di Azure Active Directory)", + "idpAzureClientIdDescription": "Il Tuo Id Client Registrazione App Azure", + "idpAzureClientSecretDescription": "Il Tuo Client Di Registrazione App Azure Secret", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Configurazione Google", + "idpAzureConfigurationTitle": "Configurazione Azure Entra ID", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Il Tuo Id Client Registrazione App Azure", + "idpAzureClientSecretDescription2": "Il Tuo Client Di Registrazione App Azure Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Intestazioni Personalizzate", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.", + "subnet": "Sottorete", + "subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.", + "authPage": "Pagina Autenticazione", + "authPageDescription": "Configura la pagina di autenticazione per la tua organizzazione", + "authPageDomain": "Dominio Pagina Auth", + "noDomainSet": "Nessun dominio impostato", + "changeDomain": "Cambia Dominio", + "selectDomain": "Seleziona Dominio", + "restartCertificate": "Riavvia Certificato", + "editAuthPageDomain": "Modifica Dominio Pagina Auth", + "setAuthPageDomain": "Imposta Dominio Pagina Autenticazione", + "failedToFetchCertificate": "Recupero del certificato non riuscito", + "failedToRestartCertificate": "Riavvio del certificato non riuscito", + "addDomainToEnableCustomAuthPages": "Aggiungi un dominio per abilitare le pagine di autenticazione personalizzate per la tua organizzazione", + "selectDomainForOrgAuthPage": "Seleziona un dominio per la pagina di autenticazione dell'organizzazione", "domainPickerProvidedDomain": "Dominio Fornito", "domainPickerFreeProvidedDomain": "Dominio Fornito Gratuito", "domainPickerVerified": "Verificato", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" non può essere reso valido per {domain}.", "domainPickerSubdomainSanitized": "Sottodominio igienizzato", "domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"", + "orgAuthSignInTitle": "Accedi alla tua organizzazione", + "orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare", + "orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.", + "orgAuthSignInWithPangolin": "Accedi con Pangolino", + "subscriptionRequiredToUse": "Per utilizzare questa funzionalità è necessario un abbonamento.", + "idpDisabled": "I provider di identità sono disabilitati.", + "orgAuthPageDisabled": "La pagina di autenticazione dell'organizzazione è disabilitata.", + "domainRestartedDescription": "Verifica del dominio riavviata con successo", "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", - "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui." } diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 30865f1e..4684eacb 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -168,6 +168,9 @@ "siteSelect": "사이트 선택", "siteSearch": "사이트 검색", "siteNotFound": "사이트를 찾을 수 없습니다.", + "selectCountry": "국가 선택하기", + "searchCountries": "국가 검색...", + "noCountryFound": "국가를 찾을 수 없습니다.", "siteSelectionDescription": "이 사이트는 대상에 대한 연결을 제공합니다.", "resourceType": "리소스 유형", "resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "연결됨", "idpErrorConnectingTo": "{name}에 연결하는 데 문제가 발생했습니다. 관리자에게 문의하십시오.", "idpErrorNotFound": "IdP를 찾을 수 없습니다.", - "idpGoogleAlt": "구글", - "idpAzureAlt": "애저", "inviteInvalid": "유효하지 않은 초대", "inviteInvalidDescription": "초대 링크가 유효하지 않습니다.", "inviteErrorWrongUser": "이 초대는 이 사용자에게 해당되지 않습니다", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "서브도메인: {subdomain}", "domainPickerNamespace": "이름 공간: {namespace}", "domainPickerShowMore": "더보기", + "regionSelectorTitle": "지역 선택", + "regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.", + "regionSelectorPlaceholder": "지역 선택", + "regionSelectorComingSoon": "곧 출시 예정", + "billingLoadingSubscription": "구독 불러오는 중...", + "billingFreeTier": "무료 티어", + "billingWarningOverLimit": "경고: 하나 이상의 사용 한도를 초과했습니다. 구독을 수정하거나 사용량을 조정하기 전까지 사이트는 연결되지 않습니다.", + "billingUsageLimitsOverview": "사용 한도 개요", + "billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@fossorial.io로 연락하십시오.", + "billingDataUsage": "데이터 사용량", + "billingOnlineTime": "사이트 온라인 시간", + "billingUsers": "활성 사용자", + "billingDomains": "활성 도메인", + "billingRemoteExitNodes": "활성 자체 호스팅 노드", + "billingNoLimitConfigured": "구성된 한도가 없습니다.", + "billingEstimatedPeriod": "예상 청구 기간", + "billingIncludedUsage": "포함 사용량", + "billingIncludedUsageDescription": "현재 구독 계획에 포함된 사용량", + "billingFreeTierIncludedUsage": "무료 티어 사용 허용량", + "billingIncluded": "포함됨", + "billingEstimatedTotal": "예상 총액:", + "billingNotes": "노트", + "billingEstimateNote": "현재 사용량을 기반으로 한 추정치입니다.", + "billingActualChargesMayVary": "실제 청구 금액은 다를 수 있습니다.", + "billingBilledAtEnd": "청구 기간이 끝난 후 청구됩니다.", + "billingModifySubscription": "구독 수정", + "billingStartSubscription": "구독 시작", + "billingRecurringCharge": "반복 요금", + "billingManageSubscriptionSettings": "구독 설정 및 기본 설정을 관리합니다", + "billingNoActiveSubscription": "활성 구독이 없습니다. 사용 한도를 늘리려면 구독을 시작하십시오.", + "billingFailedToLoadSubscription": "구독을 불러오는 데 실패했습니다.", + "billingFailedToLoadUsage": "사용량을 불러오는 데 실패했습니다.", + "billingFailedToGetCheckoutUrl": "체크아웃 URL을 가져오는 데 실패했습니다.", + "billingPleaseTryAgainLater": "나중에 다시 시도하십시오.", + "billingCheckoutError": "체크아웃 오류", + "billingFailedToGetPortalUrl": "포털 URL을 가져오는 데 실패했습니다.", + "billingPortalError": "포털 오류", + "billingDataUsageInfo": "클라우드에 연결할 때 보안 터널을 통해 전송된 모든 데이터에 대해 비용이 청구됩니다. 여기에는 모든 사이트의 들어오고 나가는 트래픽이 포함됩니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용하는 경우 데이터는 요금이 청구되지 않습니다.", + "billingOnlineTimeInfo": "사이트가 클라우드에 연결된 시간에 따라 요금이 청구됩니다. 예를 들어, 44,640분은 사이트가 한 달 내내 24시간 작동하는 것과 같습니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용할 때 시간은 요금이 청구되지 않습니다.", + "billingUsersInfo": "조직의 사용자마다 요금이 청구됩니다. 청구는 조직의 활성 사용자 계정 수에 따라 매일 계산됩니다.", + "billingDomainInfo": "조직의 도메인마다 요금이 청구됩니다. 청구는 조직의 활성 도메인 계정 수에 따라 매일 계산됩니다.", + "billingRemoteExitNodesInfo": "조직의 관리 노드마다 요금이 청구됩니다. 청구는 조직의 활성 관리 노드 수에 따라 매일 계산됩니다.", "domainNotFound": "도메인을 찾을 수 없습니다", "domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.", "failed": "실패", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 변경 사항은 인터넷 전체에 전파되는 데 시간이 걸립니다. DNS 제공자와 TTL 설정에 따라 몇 분에서 48시간까지 걸릴 수 있습니다.", "resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다", "resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요", + "billingPricingCalculatorLink": "가격 계산기", "signUpTerms": { "IAgreeToThe": "동의합니다", "termsOfService": "서비스 약관", @@ -1368,6 +1412,41 @@ "addNewTarget": "새 대상 추가", "targetsList": "대상 목록", "targetErrorDuplicateTargetFound": "중복 대상 발견", + "healthCheckHealthy": "정상", + "healthCheckUnhealthy": "비정상", + "healthCheckUnknown": "알 수 없음", + "healthCheck": "상태 확인", + "configureHealthCheck": "상태 확인 설정", + "configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정", + "enableHealthChecks": "상태 확인 활성화", + "enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.", + "healthScheme": "방법", + "healthSelectScheme": "방법 선택", + "healthCheckPath": "경로", + "healthHostname": "IP / 호스트", + "healthPort": "포트", + "healthCheckPathDescription": "상태 확인을 위한 경로입니다.", + "healthyIntervalSeconds": "정상 간격", + "unhealthyIntervalSeconds": "비정상 간격", + "IntervalSeconds": "정상 간격", + "timeoutSeconds": "시간 초과", + "timeIsInSeconds": "시간은 초 단위입니다", + "retryAttempts": "재시도 횟수", + "expectedResponseCodes": "예상 응답 코드", + "expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.", + "customHeaders": "사용자 정의 헤더", + "customHeadersDescription": "헤더는 새 줄로 구분됨: Header-Name: value", + "headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.", + "saveHealthCheck": "상태 확인 저장", + "healthCheckSaved": "상태 확인이 저장되었습니다.", + "healthCheckSavedDescription": "상태 확인 구성이 성공적으로 저장되었습니다", + "healthCheckError": "상태 확인 오류", + "healthCheckErrorDescription": "상태 확인 구성을 저장하는 동안 오류가 발생했습니다", + "healthCheckPathRequired": "상태 확인 경로는 필수입니다.", + "healthCheckMethodRequired": "HTTP 방법은 필수입니다.", + "healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.", + "healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.", + "healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.", "httpMethod": "HTTP 메소드", "selectHttpMethod": "HTTP 메소드 선택", "domainPickerSubdomainLabel": "서브도메인", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "사용 가능한 무료 도메인에서 검색 및 선택할 서브도메인 입력.", "domainPickerFreeDomains": "무료 도메인", "domainPickerSearchForAvailableDomains": "사용 가능한 도메인 검색", + "domainPickerNotWorkSelfHosted": "참고: 무료 제공 도메인은 현재 자체 호스팅 인스턴스에 사용할 수 없습니다.", "resourceDomain": "도메인", "resourceEditDomain": "도메인 수정", "siteName": "사이트 이름", @@ -1463,6 +1543,72 @@ "autoLoginError": "자동 로그인 오류", "autoLoginErrorNoRedirectUrl": "ID 공급자로부터 리디렉션 URL을 받지 못했습니다.", "autoLoginErrorGeneratingUrl": "인증 URL 생성 실패.", + "remoteExitNodeManageRemoteExitNodes": "관리 자체 호스팅", + "remoteExitNodeDescription": "네트워크 연결성을 확장하기 위해 노드를 관리하세요", + "remoteExitNodes": "노드", + "searchRemoteExitNodes": "노드 검색...", + "remoteExitNodeAdd": "노드 추가", + "remoteExitNodeErrorDelete": "노드 삭제 오류", + "remoteExitNodeQuestionRemove": "조직에서 노드 {selectedNode}를 제거하시겠습니까?", + "remoteExitNodeMessageRemove": "한 번 제거되면 더 이상 노드에 접근할 수 없습니다.", + "remoteExitNodeMessageConfirm": "확인을 위해 아래에 노드 이름을 입력해 주세요.", + "remoteExitNodeConfirmDelete": "노드 삭제 확인", + "remoteExitNodeDelete": "노드 삭제", + "sidebarRemoteExitNodes": "노드", + "remoteExitNodeCreate": { + "title": "노드 생성", + "description": "네트워크 연결성을 확장하기 위해 새 노드를 생성하세요", + "viewAllButton": "모든 노드 보기", + "strategy": { + "title": "생성 전략", + "description": "노드를 직접 구성하거나 새 자격 증명을 생성하려면 이것을 선택하세요.", + "adopt": { + "title": "노드 채택", + "description": "이미 노드의 자격 증명이 있는 경우 이것을 선택하세요." + }, + "generate": { + "title": "키 생성", + "description": "노드에 대한 새 키를 생성하려면 이것을 선택하세요" + } + }, + "adopt": { + "title": "기존 노드 채택", + "description": "채택하려는 기존 노드의 자격 증명을 입력하세요", + "nodeIdLabel": "노드 ID", + "nodeIdDescription": "채택하려는 기존 노드의 ID", + "secretLabel": "비밀", + "secretDescription": "기존 노드의 비밀 키", + "submitButton": "노드 채택" + }, + "generate": { + "title": "생성된 자격 증명", + "description": "생성된 자격 증명을 사용하여 노드를 구성하세요", + "nodeIdTitle": "노드 ID", + "secretTitle": "비밀", + "saveCredentialsTitle": "구성에 자격 증명 추가", + "saveCredentialsDescription": "연결을 완료하려면 이러한 자격 증명을 자체 호스팅 Pangolin 노드 구성 파일에 추가하십시오.", + "submitButton": "노드 생성" + }, + "validation": { + "adoptRequired": "기존 노드를 채택하려면 노드 ID와 비밀 키가 필요합니다" + }, + "errors": { + "loadDefaultsFailed": "기본값 로드 실패", + "defaultsNotLoaded": "기본값 로드되지 않음", + "createFailed": "노드 생성 실패" + }, + "success": { + "created": "노드가 성공적으로 생성되었습니다" + } + }, + "remoteExitNodeSelection": "노드 선택", + "remoteExitNodeSelectionDescription": "이 로컬 사이트에서 트래픽을 라우팅할 노드를 선택하세요", + "remoteExitNodeRequired": "로컬 사이트에 노드를 선택해야 합니다", + "noRemoteExitNodesAvailable": "사용 가능한 노드가 없습니다", + "noRemoteExitNodesAvailableDescription": "이 조직에 사용 가능한 노드가 없습니다. 로컬 사이트를 사용하려면 먼저 노드를 생성하세요.", + "exitNode": "종단 노드", + "country": "국가", + "rulesMatchCountry": "현재 소스 IP를 기반으로 합니다", "managedSelfHosted": { "title": "관리 자체 호스팅", "description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "국제 도메인 감지됨", "willbestoredas": "다음으로 저장됩니다:", + "roleMappingDescription": "자동 프로비저닝이 활성화되면 사용자가 로그인할 때 역할이 할당되는 방법을 결정합니다.", + "selectRole": "역할 선택", + "roleMappingExpression": "표현식", + "selectRolePlaceholder": "역할 선택", + "selectRoleDescription": "이 신원 공급자로부터 모든 사용자에게 할당할 역할을 선택하십시오.", + "roleMappingExpressionDescription": "ID 토큰에서 역할 정보를 추출하기 위한 JMESPath 표현식을 입력하세요.", + "idpTenantIdRequired": "테넌트 ID가 필요합니다", + "invalidValue": "잘못된 값", + "idpTypeLabel": "신원 공급자 유형", + "roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google 구성", + "idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.", + "idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID", + "idpGoogleClientSecretDescription": "Google OAuth2 클라이언트 비밀", + "idpAzureConfiguration": "Azure Entra ID 구성", + "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 자격 증명을 구성합니다.", + "idpTenantId": "테넌트 ID", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Azure 액티브 디렉터리 개요에서 찾은 Azure 테넌트 ID", + "idpAzureClientIdDescription": "Azure 앱 등록 클라이언트 ID", + "idpAzureClientSecretDescription": "Azure 앱 등록 클라이언트 비밀", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "구글", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "애저", + "idpGoogleConfigurationTitle": "Google 구성", + "idpAzureConfigurationTitle": "Azure Entra ID 구성", + "idpTenantIdLabel": "테넌트 ID", + "idpAzureClientIdDescription2": "Azure 앱 등록 클라이언트 ID", + "idpAzureClientSecretDescription2": "Azure 앱 등록 클라이언트 비밀", "idpGoogleDescription": "Google OAuth2/OIDC 공급자", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자", - "customHeaders": "사용자 정의 헤더", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.", + "subnet": "서브넷", + "subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.", + "authPage": "인증 페이지", + "authPageDescription": "조직에 대한 인증 페이지를 구성합니다.", + "authPageDomain": "인증 페이지 도메인", + "noDomainSet": "도메인 설정 없음", + "changeDomain": "도메인 변경", + "selectDomain": "도메인 선택", + "restartCertificate": "인증서 재시작", + "editAuthPageDomain": "인증 페이지 도메인 편집", + "setAuthPageDomain": "인증 페이지 도메인 설정", + "failedToFetchCertificate": "인증서 가져오기 실패", + "failedToRestartCertificate": "인증서 재시작 실패", + "addDomainToEnableCustomAuthPages": "조직의 맞춤 인증 페이지를 활성화하려면 도메인을 추가하세요.", + "selectDomainForOrgAuthPage": "조직 인증 페이지에 대한 도메인을 선택하세요.", "domainPickerProvidedDomain": "제공된 도메인", "domainPickerFreeProvidedDomain": "무료 제공된 도메인", "domainPickerVerified": "검증됨", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\"을(를) {domain}에 대해 유효하게 만들 수 없습니다.", "domainPickerSubdomainSanitized": "하위 도메인 정리됨", "domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다", + "orgAuthSignInTitle": "조직에 로그인", + "orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.", + "orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.", + "orgAuthSignInWithPangolin": "Pangolin으로 로그인", + "subscriptionRequiredToUse": "이 기능을 사용하려면 구독이 필요합니다.", + "idpDisabled": "신원 공급자가 비활성화되었습니다.", + "orgAuthPageDisabled": "조직 인증 페이지가 비활성화되었습니다.", + "domainRestartedDescription": "도메인 인증이 성공적으로 재시작되었습니다.", "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", - "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요." } diff --git a/messages/nb-NO.json b/messages/nb-NO.json index fc0cfb0b..686a6f4d 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -118,7 +118,7 @@ "usageExamples": "Brukseksempler", "tokenId": "Token-ID", "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", + "queryParameter": "Forespørsel Params", "importantNote": "Viktig merknad", "shareImportantDescription": "Av sikkerhetsgrunner anbefales det å bruke headere fremfor query parametere der det er mulig, da query parametere kan logges i serverlogger eller nettleserhistorikk.", "token": "Token", @@ -168,6 +168,9 @@ "siteSelect": "Velg område", "siteSearch": "Søk i område", "siteNotFound": "Ingen område funnet.", + "selectCountry": "Velg land", + "searchCountries": "Søk land...", + "noCountryFound": "Ingen land funnet.", "siteSelectionDescription": "Dette området vil gi tilkobling til mål.", "resourceType": "Ressurstype", "resourceTypeDescription": "Bestem hvordan du vil få tilgang til ressursen din", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Tilkoblet", "idpErrorConnectingTo": "Det oppstod et problem med å koble til {name}. Vennligst kontakt din administrator.", "idpErrorNotFound": "IdP ikke funnet", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Ugyldig invitasjon", "inviteInvalidDescription": "Invitasjonslenken er ugyldig.", "inviteErrorWrongUser": "Invitasjonen er ikke for denne brukeren", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Underdomene: {subdomain}", "domainPickerNamespace": "Navnerom: {namespace}", "domainPickerShowMore": "Vis mer", + "regionSelectorTitle": "Velg Region", + "regionSelectorInfo": "Å velge en region hjelper oss med å gi bedre ytelse for din lokasjon. Du trenger ikke være i samme region som serveren.", + "regionSelectorPlaceholder": "Velg en region", + "regionSelectorComingSoon": "Kommer snart", + "billingLoadingSubscription": "Laster abonnement...", + "billingFreeTier": "Gratis nivå", + "billingWarningOverLimit": "Advarsel: Du har overskredet en eller flere bruksgrenser. Nettstedene dine vil ikke koble til før du endrer abonnementet ditt eller justerer bruken.", + "billingUsageLimitsOverview": "Oversikt over bruksgrenser", + "billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@fossorial.io.", + "billingDataUsage": "Databruk", + "billingOnlineTime": "Online tid for nettsteder", + "billingUsers": "Aktive brukere", + "billingDomains": "Aktive domener", + "billingRemoteExitNodes": "Aktive selvstyrte noder", + "billingNoLimitConfigured": "Ingen grense konfigurert", + "billingEstimatedPeriod": "Estimert faktureringsperiode", + "billingIncludedUsage": "Inkludert Bruk", + "billingIncludedUsageDescription": "Bruk inkludert i din nåværende abonnementsplan", + "billingFreeTierIncludedUsage": "Gratis nivå bruksgrenser", + "billingIncluded": "inkludert", + "billingEstimatedTotal": "Estimert Totalt:", + "billingNotes": "Notater", + "billingEstimateNote": "Dette er et estimat basert på din nåværende bruk.", + "billingActualChargesMayVary": "Faktiske kostnader kan variere.", + "billingBilledAtEnd": "Du vil bli fakturert ved slutten av faktureringsperioden.", + "billingModifySubscription": "Endre abonnement", + "billingStartSubscription": "Start abonnement", + "billingRecurringCharge": "Innkommende Avgift", + "billingManageSubscriptionSettings": "Administrer abonnementsinnstillinger og preferanser", + "billingNoActiveSubscription": "Du har ikke et aktivt abonnement. Start abonnementet ditt for å øke bruksgrensene.", + "billingFailedToLoadSubscription": "Klarte ikke å laste abonnement", + "billingFailedToLoadUsage": "Klarte ikke å laste bruksdata", + "billingFailedToGetCheckoutUrl": "Mislyktes å få betalingslenke", + "billingPleaseTryAgainLater": "Vennligst prøv igjen senere.", + "billingCheckoutError": "Kasserror", + "billingFailedToGetPortalUrl": "Mislyktes å hente portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "Du er ladet for all data som overføres gjennom dine sikre tunneler når du er koblet til skyen. Dette inkluderer både innkommende og utgående trafikk på alle dine nettsteder. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Data belastes ikke ved bruk av EK-grupper.", + "billingOnlineTimeInfo": "Du er ladet på hvor lenge sidene dine forblir koblet til skyen. For eksempel tilsvarer 44,640 minutter ett nettsted som går 24/7 i en hel måned. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Tid belastes ikke når du bruker noder.", + "billingUsersInfo": "Du belastes for hver bruker i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive brukerkontoer i organisasjonen din.", + "billingDomainInfo": "Du belastes for hvert domene i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive domenekontoer i organisasjonen din.", + "billingRemoteExitNodesInfo": "Du belastes for hver styrt node i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive styrte noder i organisasjonen din.", "domainNotFound": "Domene ikke funnet", "domainNotFoundDescription": "Denne ressursen er deaktivert fordi domenet ikke lenger eksisterer i systemet vårt. Vennligst angi et nytt domene for denne ressursen.", "failed": "Mislyktes", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-endringer kan ta litt tid å propagere over internett. Dette kan ta fra noen få minutter til 48 timer, avhengig av din DNS-leverandør og TTL-innstillinger.", "resourcePortRequired": "Portnummer er påkrevd for ikke-HTTP-ressurser", "resourcePortNotAllowed": "Portnummer skal ikke angis for HTTP-ressurser", + "billingPricingCalculatorLink": "Pris Kalkulator", "signUpTerms": { "IAgreeToThe": "Jeg godtar", "termsOfService": "brukervilkårene", @@ -1368,6 +1412,41 @@ "addNewTarget": "Legg til nytt mål", "targetsList": "Liste over mål", "targetErrorDuplicateTargetFound": "Duplikat av mål funnet", + "healthCheckHealthy": "Sunn", + "healthCheckUnhealthy": "Usunn", + "healthCheckUnknown": "Ukjent", + "healthCheck": "Helsekontroll", + "configureHealthCheck": "Konfigurer Helsekontroll", + "configureHealthCheckDescription": "Sett opp helsekontroll for {target}", + "enableHealthChecks": "Aktiver Helsekontroller", + "enableHealthChecksDescription": "Overvåk helsen til dette målet. Du kan overvåke et annet endepunkt enn målet hvis nødvendig.", + "healthScheme": "Metode", + "healthSelectScheme": "Velg metode", + "healthCheckPath": "Sti", + "healthHostname": "IP / Vert", + "healthPort": "Port", + "healthCheckPathDescription": "Stien for å sjekke helsestatus.", + "healthyIntervalSeconds": "Sunt intervall", + "unhealthyIntervalSeconds": "Usunt intervall", + "IntervalSeconds": "Sunt intervall", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Tid er i sekunder", + "retryAttempts": "Forsøk på nytt", + "expectedResponseCodes": "Forventede svarkoder", + "expectedResponseCodesDescription": "HTTP-statuskode som indikerer sunn status. Hvis den blir stående tom, regnes 200-300 som sunn.", + "customHeaders": "Egendefinerte topptekster", + "customHeadersDescription": "Overskrifter som er adskilt med linje: Overskriftsnavn: verdi", + "headersValidationError": "Topptekst må være i formatet: header-navn: verdi.", + "saveHealthCheck": "Lagre Helsekontroll", + "healthCheckSaved": "Helsekontroll Lagret", + "healthCheckSavedDescription": "Helsekontrollkonfigurasjonen ble lagret", + "healthCheckError": "Helsekontrollfeil", + "healthCheckErrorDescription": "Det oppstod en feil under lagring av helsekontrollkonfigurasjonen", + "healthCheckPathRequired": "Helsekontrollsti er påkrevd", + "healthCheckMethodRequired": "HTTP-metode er påkrevd", + "healthCheckIntervalMin": "Sjekkeintervallet må være minst 5 sekunder", + "healthCheckTimeoutMin": "Timeout må være minst 1 sekund", + "healthCheckRetryMin": "Forsøk på nytt må være minst 1", "httpMethod": "HTTP-metode", "selectHttpMethod": "Velg HTTP-metode", "domainPickerSubdomainLabel": "Underdomene", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Skriv inn et underdomene for å søke og velge blant tilgjengelige gratis domener.", "domainPickerFreeDomains": "Gratis domener", "domainPickerSearchForAvailableDomains": "Søk etter tilgjengelige domener", + "domainPickerNotWorkSelfHosted": "Merk: Gratis tilbudte domener er ikke tilgjengelig for selv-hostede instanser akkurat nå.", "resourceDomain": "Domene", "resourceEditDomain": "Rediger domene", "siteName": "Områdenavn", @@ -1463,6 +1543,72 @@ "autoLoginError": "Feil ved automatisk innlogging", "autoLoginErrorNoRedirectUrl": "Ingen omdirigerings-URL mottatt fra identitetsleverandøren.", "autoLoginErrorGeneratingUrl": "Kunne ikke generere autentiserings-URL.", + "remoteExitNodeManageRemoteExitNodes": "Administrer Selv-Hostet", + "remoteExitNodeDescription": "Administrer noder for å forlenge nettverkstilkoblingen din", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Søk noder...", + "remoteExitNodeAdd": "Legg til Node", + "remoteExitNodeErrorDelete": "Feil ved sletting av node", + "remoteExitNodeQuestionRemove": "Er du sikker på at du vil fjerne noden {selectedNode} fra organisasjonen?", + "remoteExitNodeMessageRemove": "Når noden er fjernet, vil ikke lenger være tilgjengelig.", + "remoteExitNodeMessageConfirm": "For å bekrefte, skriv inn navnet på noden nedenfor.", + "remoteExitNodeConfirmDelete": "Bekreft sletting av Node", + "remoteExitNodeDelete": "Slett Node", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Opprett node", + "description": "Opprett en ny node for å utvide nettverkstilkoblingen din", + "viewAllButton": "Vis alle koder", + "strategy": { + "title": "Opprettelsesstrategi", + "description": "Velg denne for manuelt å konfigurere noden eller generere nye legitimasjoner.", + "adopt": { + "title": "Adopter Node", + "description": "Velg dette hvis du allerede har legitimasjon til noden." + }, + "generate": { + "title": "Generer Nøkler", + "description": "Velg denne hvis du vil generere nye nøkler for noden" + } + }, + "adopt": { + "title": "Adopter Eksisterende Node", + "description": "Skriv inn opplysningene til den eksisterende noden du vil adoptere", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "ID-en til den eksisterende noden du vil adoptere", + "secretLabel": "Sikkerhetsnøkkel", + "secretDescription": "Den hemmelige nøkkelen til en eksisterende node", + "submitButton": "Adopt Node" + }, + "generate": { + "title": "Genererte Legitimasjoner", + "description": "Bruk disse genererte opplysningene for å konfigurere noden din", + "nodeIdTitle": "Node ID", + "secretTitle": "Sikkerhet", + "saveCredentialsTitle": "Legg til Legitimasjoner til Config", + "saveCredentialsDescription": "Legg til disse legitimasjonene i din selv-hostede Pangolin node-konfigurasjonsfil for å fullføre koblingen.", + "submitButton": "Opprett node" + }, + "validation": { + "adoptRequired": "Node ID og Secret er påkrevd når du adopterer en eksisterende node" + }, + "errors": { + "loadDefaultsFailed": "Feil ved lasting av standarder", + "defaultsNotLoaded": "Standarder ikke lastet", + "createFailed": "Kan ikke opprette node" + }, + "success": { + "created": "Node opprettet" + } + }, + "remoteExitNodeSelection": "Noden utvalg", + "remoteExitNodeSelectionDescription": "Velg en node for å sende trafikk gjennom for dette lokale nettstedet", + "remoteExitNodeRequired": "En node må velges for lokale nettsteder", + "noRemoteExitNodesAvailable": "Ingen noder tilgjengelig", + "noRemoteExitNodesAvailableDescription": "Ingen noder er tilgjengelige for denne organisasjonen. Opprett en node først for å bruke lokale nettsteder.", + "exitNode": "Utgangsnode", + "country": "Land", + "rulesMatchCountry": "For tiden basert på kilde IP", "managedSelfHosted": { "title": "Administrert selv-hostet", "description": "Sikre og lavvedlikeholdsservere, selvbetjente Pangolin med ekstra klokker, og understell", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Internasjonalt domene oppdaget", "willbestoredas": "Vil bli lagret som:", + "roleMappingDescription": "Bestem hvordan roller tilordnes brukere når innloggingen er aktivert når autog-rapportering er aktivert.", + "selectRole": "Velg en rolle", + "roleMappingExpression": "Uttrykk", + "selectRolePlaceholder": "Velg en rolle", + "selectRoleDescription": "Velg en rolle å tilordne alle brukere fra denne identitet leverandøren", + "roleMappingExpressionDescription": "Skriv inn et JMESPath uttrykk for å hente rolleinformasjon fra ID-nøkkelen", + "idpTenantIdRequired": "Bedriftens ID kreves", + "invalidValue": "Ugyldig verdi", + "idpTypeLabel": "Identitet leverandør type", + "roleMappingExpressionPlaceholder": "F.eks. inneholder(grupper, 'admin') && 'Admin' ⋅'Medlem'", + "idpGoogleConfiguration": "Google Konfigurasjon", + "idpGoogleConfigurationDescription": "Konfigurer din Google OAuth2 legitimasjon", + "idpGoogleClientIdDescription": "Din Google OAuth2-klient-ID", + "idpGoogleClientSecretDescription": "Google OAuth2-klienten din hemmelig", + "idpAzureConfiguration": "Azure Entra ID konfigurasjon", + "idpAzureConfigurationDescription": "Konfigurere din Azure Entra ID OAuth2 legitimasjon", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "din-tenant-id", + "idpAzureTenantIdDescription": "Din Azure leie-ID (funnet i Azure Active Directory-oversikten)", + "idpAzureClientIdDescription": "Din Azure App registrerings klient-ID", + "idpAzureClientSecretDescription": "Din Azure App registrerings klient hemmelig", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Konfigurasjon", + "idpAzureConfigurationTitle": "Azure Entra ID konfigurasjon", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Din Azure App registrerings klient-ID", + "idpAzureClientSecretDescription2": "Din Azure App registrerings klient hemmelig", "idpGoogleDescription": "Google OAuth2/OIDC leverandør", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Egendefinerte topptekster", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Topptekst må være i formatet: header-navn: verdi.", + "subnet": "Subnett", + "subnetDescription": "Undernettverket for denne organisasjonens nettverkskonfigurasjon.", + "authPage": "Autentiseringsside", + "authPageDescription": "Konfigurer autoriseringssiden for din organisasjon", + "authPageDomain": "Autentiseringsside domene", + "noDomainSet": "Ingen domene valgt", + "changeDomain": "Endre domene", + "selectDomain": "Velg domene", + "restartCertificate": "Omstart sertifikat", + "editAuthPageDomain": "Rediger auth sidedomene", + "setAuthPageDomain": "Angi autoriseringsside domene", + "failedToFetchCertificate": "Kunne ikke hente sertifikat", + "failedToRestartCertificate": "Kan ikke starte sertifikat", + "addDomainToEnableCustomAuthPages": "Legg til et domene for å aktivere egendefinerte autentiseringssider for organisasjonen din", + "selectDomainForOrgAuthPage": "Velg et domene for organisasjonens autentiseringsside", "domainPickerProvidedDomain": "Gitt domene", "domainPickerFreeProvidedDomain": "Gratis oppgitt domene", "domainPickerVerified": "Bekreftet", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kunne ikke gjøres gyldig for {domain}.", "domainPickerSubdomainSanitized": "Underdomenet som ble sanivert", "domainPickerSubdomainCorrected": "\"{sub}\" var korrigert til \"{sanitized}\"", + "orgAuthSignInTitle": "Logg inn på din organisasjon", + "orgAuthChooseIdpDescription": "Velg din identitet leverandør for å fortsette", + "orgAuthNoIdpConfigured": "Denne organisasjonen har ikke noen identitetstjeneste konfigurert. Du kan i stedet logge inn med Pangolin identiteten din.", + "orgAuthSignInWithPangolin": "Logg inn med Pangolin", + "subscriptionRequiredToUse": "Et abonnement er påkrevd for å bruke denne funksjonen.", + "idpDisabled": "Identitetsleverandører er deaktivert.", + "orgAuthPageDisabled": "Informasjons-siden for organisasjon er deaktivert.", + "domainRestartedDescription": "Domene-verifiseringen ble startet på nytt", "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", - "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her." } diff --git a/messages/nl-NL.json b/messages/nl-NL.json index ada6f687..12744579 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -10,7 +10,7 @@ "setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.", "componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.", "componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.", - "welcome": "Welkom bij Pangolin!", + "welcome": "Welkom bij Pangolin", "welcomeTo": "Welkom bij", "componentsCreateOrg": "Maak een Organisatie", "componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.", @@ -22,7 +22,7 @@ "inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.", "inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.", "inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.", - "inviteCreateUser": "U moet eerst een account aanmaken.", + "inviteCreateUser": "U moet eerst een account aanmaken", "goHome": "Ga naar huis", "inviteLogInOtherUser": "Log in als een andere gebruiker", "createAnAccount": "Account aanmaken", @@ -38,12 +38,12 @@ "name": "Naam", "online": "Online", "offline": "Offline", - "site": "Referentie", - "dataIn": "Dataverbruik inkomend", - "dataOut": "Dataverbruik uitgaand", + "site": "Website", + "dataIn": "Gegevens in", + "dataOut": "Data Uit", "connectionType": "Type verbinding", "tunnelType": "Tunnel type", - "local": "Lokaal", + "local": "lokaal", "edit": "Bewerken", "siteConfirmDelete": "Verwijderen van site bevestigen", "siteDelete": "Site verwijderen", @@ -55,7 +55,7 @@ "siteCreate": "Site maken", "siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden", "siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen", - "close": "Sluiten", + "close": "Afsluiten", "siteErrorCreate": "Fout bij maken site", "siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden", "siteErrorCreateDefaults": "Standaardinstellingen niet gevonden", @@ -90,21 +90,21 @@ "siteGeneralDescription": "Algemene instellingen voor deze site configureren", "siteSettingDescription": "Configureer de instellingen op uw site", "siteSetting": "{siteName} instellingen", - "siteNewtTunnel": "Newttunnel (Aanbevolen)", + "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", "siteWgDescriptionSaas": "Gebruik elke WireGuard-client om een tunnel op te zetten. Handmatige NAT-instelling vereist. WERKT ALLEEN OP SELF HOSTED NODES", "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", "siteLocalDescriptionSaas": "Alleen lokale bronnen. Geen tunneling. WERKT ALLEEN OP SELF HOSTED NODES", - "siteSeeAll": "Alle sites bekijken", + "siteSeeAll": "Alle werkruimtes bekijken", "siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site", "siteNewtCredentials": "Nieuwste aanmeldgegevens", "siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server", "siteCredentialsSave": "Uw referenties opslaan", "siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "siteInfo": "Site informatie", - "status": "Status", + "status": "status", "shareTitle": "Beheer deellinks", "shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen", "shareSearch": "Zoek share links...", @@ -146,19 +146,19 @@ "never": "Nooit", "shareErrorSelectResource": "Selecteer een bron", "resourceTitle": "Bronnen beheren", - "resourceDescription": "Veilige proxy's voor uw privéapplicaties maken", + "resourceDescription": "Veilige proxy's voor uw privé applicaties maken", "resourcesSearch": "Zoek bronnen...", "resourceAdd": "Bron toevoegen", "resourceErrorDelte": "Fout bij verwijderen document", "authentication": "Authenticatie", "protected": "Beschermd", - "notProtected": "Niet beveiligd", + "notProtected": "Niet beschermd", "resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.", "resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.", "resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?", "resourceHTTP": "HTTPS bron", "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", - "resourceRaw": "TCP/UDP bron", + "resourceRaw": "Ruwe TCP/UDP bron", "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", "resourceCreate": "Bron maken", "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", @@ -168,6 +168,9 @@ "siteSelect": "Selecteer site", "siteSearch": "Zoek site", "siteNotFound": "Geen site gevonden.", + "selectCountry": "Selecteer land", + "searchCountries": "Zoek landen...", + "noCountryFound": "Geen land gevonden.", "siteSelectionDescription": "Deze site zal connectiviteit met het doelwit bieden.", "resourceType": "Type bron", "resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron", @@ -183,7 +186,7 @@ "protocolSelect": "Selecteer een protocol", "resourcePortNumber": "Nummer van poort", "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", - "cancel": "Annuleren", + "cancel": "annuleren", "resourceConfig": "Configuratie tekstbouwstenen", "resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen", "resourceAddEntrypoints": "Traefik: Entrypoints toevoegen", @@ -212,7 +215,7 @@ "saveGeneralSettings": "Algemene instellingen opslaan", "saveSettings": "Instellingen opslaan", "orgDangerZone": "Gevaarlijke zone", - "orgDangerZoneDescription": "Deze instantie verwijderen is onomkeerbaar. Bevestig alstublieft dat u wilt doorgaan.", + "orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.", "orgDelete": "Verwijder organisatie", "orgDeleteConfirm": "Bevestig Verwijderen Organisatie", "orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.", @@ -265,7 +268,7 @@ "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", "apiKeysList": "Uw API-sleutel", "apiKeysSave": "Uw API-sleutel opslaan", - "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.", "apiKeysInfo": "Uw API-sleutel is:", "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", "generate": "Genereren", @@ -501,7 +504,7 @@ "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", "methodSelect": "Selecteer methode", "targetSubmit": "Doelwit toevoegen", - "targetNoOne": "Geen doel toegevoegd. Voeg deze toe via dit formulier.", + "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", "targetsSubmit": "Doelstellingen opslaan", "proxyAdditional": "Extra Proxy-instellingen", @@ -572,7 +575,7 @@ "domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen", "none": "geen", "unknown": "onbekend", - "resources": "Bronnen", + "resources": "Hulpmiddelen", "resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.", "resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling", "resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren", @@ -598,7 +601,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", - "sites": "Sites", + "sites": "Werkruimtes", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", @@ -727,22 +730,22 @@ "idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?", "idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.", "idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.", - "idpConfirmDelete": "Bevestig verwijderen identiteit provider", - "idpDelete": "Identiteit provider verwijderen", - "idp": "Identiteitsproviders", - "idpSearch": "Identiteitsproviders zoeken...", - "idpAdd": "Identiteit provider toevoegen", - "idpClientIdRequired": "Client ID is vereist.", - "idpClientSecretRequired": "Client geheim is vereist.", - "idpErrorAuthUrlInvalid": "Authenticatie URL moet een geldige URL zijn.", - "idpErrorTokenUrlInvalid": "Token URL moet een geldige URL zijn.", + "idpConfirmDelete": "Bevestig verwijderen Identity Provider", + "idpDelete": "Identity Provider verwijderen", + "idp": "Identiteit aanbieders", + "idpSearch": "Identiteitsaanbieders zoeken...", + "idpAdd": "Identity Provider toevoegen", + "idpClientIdRequired": "Client-ID is vereist.", + "idpClientSecretRequired": "Clientgeheim is vereist.", + "idpErrorAuthUrlInvalid": "Authenticatie-URL moet een geldige URL zijn.", + "idpErrorTokenUrlInvalid": "Token-URL moet een geldige URL zijn.", "idpPathRequired": "ID-pad is vereist.", "idpScopeRequired": "Toepassingsgebieden zijn vereist.", - "idpOidcDescription": "Een OpenID Connect identiteitsprovider configureren", - "idpCreatedDescription": "Identiteitsprovider succesvol aangemaakt", - "idpCreate": "Identiteitsprovider aanmaken", - "idpCreateDescription": "Een nieuwe identiteitsprovider voor authenticatie configureren", - "idpSeeAll": "Zie alle Identiteitsproviders", + "idpOidcDescription": "Een OpenID Connect identity provider configureren", + "idpCreatedDescription": "Identity provider succesvol aangemaakt", + "idpCreate": "Identity Provider aanmaken", + "idpCreateDescription": "Een nieuwe identiteitsprovider voor gebruikersauthenticatie configureren", + "idpSeeAll": "Zie alle identiteitsaanbieders", "idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider", "idpDisplayName": "Een weergavenaam voor deze identiteitsprovider", "idpAutoProvisionUsers": "Auto Provisie Gebruikers", @@ -752,10 +755,10 @@ "idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren", "idpOidcConfigure": "OAuth2/OIDC configuratie", "idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties", - "idpClientId": "Client ID", - "idpClientIdDescription": "De OAuth2 client ID van uw identiteitsprovider", - "idpClientSecret": "Client Secret", - "idpClientSecretDescription": "Het OAuth2 Client Secret van je identiteitsprovider", + "idpClientId": "Klant ID", + "idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider", + "idpClientSecret": "Clientgeheim", + "idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider", "idpAuthUrl": "URL autorisatie", "idpAuthUrlDescription": "De URL voor autorisatie OAuth2", "idpTokenUrl": "URL token", @@ -801,7 +804,7 @@ "defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.", "defaultMappingsSubmit": "Standaard toewijzingen opslaan", "orgPoliciesEdit": "Organisatie beleid bewerken", - "org": "Organisatie", + "org": "Rekening", "orgSelect": "Selecteer organisatie", "orgSearch": "Zoek in org", "orgNotFound": "Geen org gevonden.", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Verbonden", "idpErrorConnectingTo": "Er was een probleem bij het verbinden met {name}. Neem contact op met uw beheerder.", "idpErrorNotFound": "IdP niet gevonden", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Ongeldige uitnodiging", "inviteInvalidDescription": "Uitnodigingslink is ongeldig.", "inviteErrorWrongUser": "Uitnodiging is niet voor deze gebruiker", @@ -924,7 +925,7 @@ "inviteErrorExpired": "De uitnodiging is mogelijk verlopen", "inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken", "inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink", - "pangolinSetup": "Setup - Pangolin", + "pangolinSetup": "Instellen - Pangolin", "orgNameRequired": "Organisatienaam is vereist", "orgIdRequired": "Organisatie-ID is vereist", "orgErrorCreate": "Fout opgetreden tijdens het aanmaken org", @@ -976,10 +977,10 @@ "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", "githubUsername": "GitHub-gebruikersnaam", "supportKeyInput": "Supporter Sleutel", - "supportKeyBuy": "Koop supportersleutel", + "supportKeyBuy": "Koop Supportersleutel", "logoutError": "Fout bij uitloggen", "signingAs": "Ingelogd als", - "serverAdmin": "Server beheer", + "serverAdmin": "Server Beheerder", "managedSelfhosted": "Beheerde Self-Hosted", "otpEnable": "Twee-factor inschakelen", "otpDisable": "Tweestapsverificatie uitschakelen", @@ -994,12 +995,12 @@ "actionGetUser": "Gebruiker ophalen", "actionGetOrgUser": "Krijg organisatie-gebruiker", "actionListOrgDomains": "Lijst organisatie domeinen", - "actionCreateSite": "Site maken", + "actionCreateSite": "Site aanmaken", "actionDeleteSite": "Site verwijderen", "actionGetSite": "Site ophalen", "actionListSites": "Sites weergeven", "actionApplyBlueprint": "Blauwdruk toepassen", - "setupToken": "Setup Token", + "setupToken": "Instel Token", "setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.", "setupTokenRequired": "Setup-token is vereist", "actionUpdateSite": "Site bijwerken", @@ -1128,7 +1129,7 @@ "sidebarOverview": "Overzicht.", "sidebarHome": "Startpagina", "sidebarSites": "Werkruimtes", - "sidebarResources": "Bronnen", + "sidebarResources": "Hulpmiddelen", "sidebarAccessControl": "Toegangs controle", "sidebarUsers": "Gebruikers", "sidebarInvitations": "Uitnodigingen", @@ -1255,8 +1256,50 @@ "domainPickerOrganizationDomains": "Organisatiedomeinen", "domainPickerProvidedDomains": "Aangeboden domeinen", "domainPickerSubdomain": "Subdomein: {subdomain}", - "domainPickerNamespace": "Namespace: {namespace}", + "domainPickerNamespace": "Naamruimte: {namespace}", "domainPickerShowMore": "Meer weergeven", + "regionSelectorTitle": "Selecteer Regio", + "regionSelectorInfo": "Het selecteren van een regio helpt ons om betere prestaties te leveren voor uw locatie. U hoeft niet in dezelfde regio als uw server te zijn.", + "regionSelectorPlaceholder": "Kies een regio", + "regionSelectorComingSoon": "Komt binnenkort", + "billingLoadingSubscription": "Abonnement laden...", + "billingFreeTier": "Gratis Niveau", + "billingWarningOverLimit": "Waarschuwing: U hebt een of meer gebruikslimieten overschreden. Uw sites maken geen verbinding totdat u uw abonnement aanpast of uw gebruik aanpast.", + "billingUsageLimitsOverview": "Overzicht gebruikslimieten", + "billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@fossorial.io.", + "billingDataUsage": "Gegevensgebruik", + "billingOnlineTime": "Site Online Tijd", + "billingUsers": "Actieve Gebruikers", + "billingDomains": "Actieve Domeinen", + "billingRemoteExitNodes": "Actieve Zelfgehoste Nodes", + "billingNoLimitConfigured": "Geen limiet ingesteld", + "billingEstimatedPeriod": "Geschatte Facturatie Periode", + "billingIncludedUsage": "Opgenomen Gebruik", + "billingIncludedUsageDescription": "Gebruik inbegrepen in uw huidige abonnementsplan", + "billingFreeTierIncludedUsage": "Gratis niveau gebruikstoelagen", + "billingIncluded": "inbegrepen", + "billingEstimatedTotal": "Geschat Totaal:", + "billingNotes": "Notities", + "billingEstimateNote": "Dit is een schatting gebaseerd op uw huidige gebruik.", + "billingActualChargesMayVary": "Facturering kan variëren.", + "billingBilledAtEnd": "U wordt aan het einde van de factureringsperiode gefactureerd.", + "billingModifySubscription": "Abonnementsaanpassing", + "billingStartSubscription": "Abonnement Starten", + "billingRecurringCharge": "Terugkerende Kosten", + "billingManageSubscriptionSettings": "Beheer uw abonnementsinstellingen en voorkeuren", + "billingNoActiveSubscription": "U heeft geen actief abonnement. Start uw abonnement om gebruikslimieten te verhogen.", + "billingFailedToLoadSubscription": "Fout bij laden van abonnement", + "billingFailedToLoadUsage": "Niet gelukt om gebruik te laden", + "billingFailedToGetCheckoutUrl": "Niet gelukt om checkout URL te krijgen", + "billingPleaseTryAgainLater": "Probeer het later opnieuw.", + "billingCheckoutError": "Checkout Fout", + "billingFailedToGetPortalUrl": "Niet gelukt om portal URL te krijgen", + "billingPortalError": "Portal Fout", + "billingDataUsageInfo": "U bent in rekening gebracht voor alle gegevens die via uw beveiligde tunnels via de cloud worden verzonden. Dit omvat zowel inkomende als uitgaande verkeer over al uw sites. Wanneer u uw limiet bereikt zullen uw sites de verbinding verbreken totdat u uw abonnement upgradet of het gebruik vermindert. Gegevens worden niet in rekening gebracht bij het gebruik van knooppunten.", + "billingOnlineTimeInfo": "U wordt in rekening gebracht op basis van hoe lang uw sites verbonden blijven met de cloud. Bijvoorbeeld 44,640 minuten is gelijk aan één site met 24/7 voor een volledige maand. Wanneer u uw limiet bereikt, zal de verbinding tussen uw sites worden verbroken totdat u een upgrade van uw abonnement uitvoert of het gebruik vermindert. Tijd wordt niet belast bij het gebruik van knooppunten.", + "billingUsersInfo": "U wordt gefactureerd voor elke gebruiker in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve gebruikersaccounts in uw organisatie.", + "billingDomainInfo": "U wordt gefactureerd voor elk domein in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve domeinaccounts in uw organisatie.", + "billingRemoteExitNodesInfo": "U wordt gefactureerd voor elke beheerde Node in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve beheerde Nodes in uw organisatie.", "domainNotFound": "Domein niet gevonden", "domainNotFoundDescription": "Deze bron is uitgeschakeld omdat het domein niet langer in ons systeem bestaat. Stel een nieuw domein in voor deze bron.", "failed": "Mislukt", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-wijzigingen kunnen enige tijd duren om over het internet te worden verspreid. Dit kan enkele minuten tot 48 uur duren, afhankelijk van je DNS-provider en TTL-instellingen.", "resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen", "resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen", + "billingPricingCalculatorLink": "Prijs Calculator", "signUpTerms": { "IAgreeToThe": "Ik ga akkoord met de", "termsOfService": "servicevoorwaarden", @@ -1368,6 +1412,41 @@ "addNewTarget": "Voeg nieuw doelwit toe", "targetsList": "Lijst met doelen", "targetErrorDuplicateTargetFound": "Dubbel doelwit gevonden", + "healthCheckHealthy": "Gezond", + "healthCheckUnhealthy": "Ongezond", + "healthCheckUnknown": "Onbekend", + "healthCheck": "Gezondheidscontrole", + "configureHealthCheck": "Configureer Gezondheidscontrole", + "configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in", + "enableHealthChecks": "Inschakelen Gezondheidscontroles", + "enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.", + "healthScheme": "Methode", + "healthSelectScheme": "Selecteer methode", + "healthCheckPath": "Pad", + "healthHostname": "IP / Host", + "healthPort": "Poort", + "healthCheckPathDescription": "Het pad om de gezondheid status te controleren.", + "healthyIntervalSeconds": "Gezonde Interval", + "unhealthyIntervalSeconds": "Ongezonde Interval", + "IntervalSeconds": "Gezonde Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Tijd is in seconden", + "retryAttempts": "Herhaal Pogingen", + "expectedResponseCodes": "Verwachte Reactiecodes", + "expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.", + "customHeaders": "Aangepaste headers", + "customHeadersDescription": "Kopregeleinde: Header-Naam: waarde", + "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", + "saveHealthCheck": "Opslaan Gezondheidscontrole", + "healthCheckSaved": "Gezondheidscontrole Opgeslagen", + "healthCheckSavedDescription": "Gezondheidscontrole configuratie succesvol opgeslagen", + "healthCheckError": "Gezondheidscontrole Fout", + "healthCheckErrorDescription": "Er is een fout opgetreden bij het opslaan van de configuratie van de gezondheidscontrole.", + "healthCheckPathRequired": "Gezondheidscontrole pad is vereist", + "healthCheckMethodRequired": "HTTP methode is vereist", + "healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn", + "healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn", + "healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn", "httpMethod": "HTTP-methode", "selectHttpMethod": "Selecteer HTTP-methode", "domainPickerSubdomainLabel": "Subdomein", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Voer een subdomein in om te zoeken en te selecteren uit beschikbare gratis domeinen.", "domainPickerFreeDomains": "Gratis Domeinen", "domainPickerSearchForAvailableDomains": "Zoek naar beschikbare domeinen", + "domainPickerNotWorkSelfHosted": "Opmerking: Gratis aangeboden domeinen zijn momenteel niet beschikbaar voor zelf-gehoste instanties.", "resourceDomain": "Domein", "resourceEditDomain": "Domein bewerken", "siteName": "Site Naam", @@ -1463,6 +1543,72 @@ "autoLoginError": "Auto Login Fout", "autoLoginErrorNoRedirectUrl": "Geen redirect URL ontvangen van de identity provider.", "autoLoginErrorGeneratingUrl": "Genereren van authenticatie-URL mislukt.", + "remoteExitNodeManageRemoteExitNodes": "Beheer Zelf-Gehoste", + "remoteExitNodeDescription": "Beheer knooppunten om uw netwerkverbinding uit te breiden", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Knooppunten zoeken...", + "remoteExitNodeAdd": "Voeg node toe", + "remoteExitNodeErrorDelete": "Fout bij verwijderen node", + "remoteExitNodeQuestionRemove": "Weet u zeker dat u het node {selectedNode} uit de organisatie wilt verwijderen?", + "remoteExitNodeMessageRemove": "Eenmaal verwijderd, zal het knooppunt niet langer toegankelijk zijn.", + "remoteExitNodeMessageConfirm": "Om te bevestigen, typ de naam van het knooppunt hieronder.", + "remoteExitNodeConfirmDelete": "Bevestig verwijderen node", + "remoteExitNodeDelete": "Knoop verwijderen", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Maak node", + "description": "Maak een nieuwe node aan om uw netwerkverbinding uit te breiden", + "viewAllButton": "Alle nodes weergeven", + "strategy": { + "title": "Creatie Strategie", + "description": "Kies dit om uw node handmatig te configureren of nieuwe referenties te genereren.", + "adopt": { + "title": "Adopteer Node", + "description": "Kies dit als u al de referenties voor deze node heeft" + }, + "generate": { + "title": "Genereer Sleutels", + "description": "Kies dit als u nieuwe sleutels voor het knooppunt wilt genereren" + } + }, + "adopt": { + "title": "Adopteer Bestaande Node", + "description": "Voer de referenties in van het bestaande knooppunt dat u wilt adopteren", + "nodeIdLabel": "Knooppunt ID", + "nodeIdDescription": "De ID van het knooppunt dat u wilt adopteren", + "secretLabel": "Geheim", + "secretDescription": "De geheime sleutel van de bestaande node", + "submitButton": "Knooppunt adopteren" + }, + "generate": { + "title": "Gegeneerde Inloggegevens", + "description": "Gebruik deze gegenereerde inloggegevens om uw node te configureren", + "nodeIdTitle": "Knooppunt ID", + "secretTitle": "Geheim", + "saveCredentialsTitle": "Voeg Inloggegevens toe aan Config", + "saveCredentialsDescription": "Voeg deze inloggegevens toe aan uw zelf-gehoste Pangolin-node configuratiebestand om de verbinding te voltooien.", + "submitButton": "Maak node" + }, + "validation": { + "adoptRequired": "Node ID en Secret zijn verplicht bij het overnemen van een bestaand knooppunt" + }, + "errors": { + "loadDefaultsFailed": "Niet gelukt om standaarden te laden", + "defaultsNotLoaded": "Standaarden niet geladen", + "createFailed": "Fout bij het maken van node" + }, + "success": { + "created": "Node succesvol aangemaakt" + } + }, + "remoteExitNodeSelection": "Knooppunt selectie", + "remoteExitNodeSelectionDescription": "Selecteer een node om het verkeer door te leiden voor deze lokale site", + "remoteExitNodeRequired": "Een node moet worden geselecteerd voor lokale sites", + "noRemoteExitNodesAvailable": "Geen knooppunten beschikbaar", + "noRemoteExitNodesAvailableDescription": "Er zijn geen knooppunten beschikbaar voor deze organisatie. Maak eerst een knooppunt aan om lokale sites te gebruiken.", + "exitNode": "Exit Node", + "country": "Land", + "rulesMatchCountry": "Momenteel gebaseerd op bron IP", "managedSelfHosted": { "title": "Beheerde Self-Hosted", "description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Internationaal Domein Gedetecteerd", "willbestoredas": "Zal worden opgeslagen als:", + "roleMappingDescription": "Bepaal hoe rollen worden toegewezen aan gebruikers wanneer ze inloggen wanneer Auto Provision is ingeschakeld.", + "selectRole": "Selecteer een rol", + "roleMappingExpression": "Expressie", + "selectRolePlaceholder": "Kies een rol", + "selectRoleDescription": "Selecteer een rol om toe te wijzen aan alle gebruikers van deze identiteitsprovider", + "roleMappingExpressionDescription": "Voer een JMESPath expressie in om rolinformatie van de ID-token te extraheren", + "idpTenantIdRequired": "Tenant ID is vereist", + "invalidValue": "Ongeldige waarde", + "idpTypeLabel": "Identiteit provider type", + "roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'", + "idpGoogleConfiguration": "Google Configuratie", + "idpGoogleConfigurationDescription": "Configureer uw Google OAuth2-referenties", + "idpGoogleClientIdDescription": "Uw Google OAuth2-client-ID", + "idpGoogleClientSecretDescription": "Uw Google OAuth2 Clientgeheim", + "idpAzureConfiguration": "Azure Entra ID configuratie", + "idpAzureConfigurationDescription": "Configureer uw Azure Entra ID OAuth2 referenties", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "jouw-tenant-id", + "idpAzureTenantIdDescription": "Uw Azure tenant ID (gevonden in Azure Active Directory overzicht)", + "idpAzureClientIdDescription": "Uw Azure App registratie Client ID", + "idpAzureClientSecretDescription": "Uw Azure App registratie Client Secret", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Configuratie", + "idpAzureConfigurationTitle": "Azure Entra ID configuratie", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Uw Azure App registratie Client ID", + "idpAzureClientSecretDescription2": "Uw Azure App registratie Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Aangepaste headers", - "customHeadersDescription": "Voeg aangepaste headers toe die met proxyverzoeken worden meegestuurd. Gebruik één regel per header in het formaat 'Header-naam: waarde'", - "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", + "subnet": "Subnet", + "subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.", + "authPage": "Authenticatie pagina", + "authPageDescription": "De autorisatiepagina voor uw organisatie configureren", + "authPageDomain": "Authenticatie pagina domein", + "noDomainSet": "Geen domein ingesteld", + "changeDomain": "Domein wijzigen", + "selectDomain": "Domein selecteren", + "restartCertificate": "Certificaat opnieuw starten", + "editAuthPageDomain": "Authenticatiepagina domein bewerken", + "setAuthPageDomain": "Authenticatiepagina domein instellen", + "failedToFetchCertificate": "Certificaat ophalen mislukt", + "failedToRestartCertificate": "Kon certificaat niet opnieuw opstarten", + "addDomainToEnableCustomAuthPages": "Voeg een domein toe om aangepaste authenticatiepagina's voor uw organisatie in te schakelen", + "selectDomainForOrgAuthPage": "Selecteer een domein voor de authenticatiepagina van de organisatie", "domainPickerProvidedDomain": "Opgegeven domein", "domainPickerFreeProvidedDomain": "Gratis verstrekt domein", "domainPickerVerified": "Geverifieerd", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kon niet geldig worden gemaakt voor {domain}.", "domainPickerSubdomainSanitized": "Subdomein gesaniseerd", "domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"", + "orgAuthSignInTitle": "Meld je aan bij je organisatie", + "orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan", + "orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.", + "orgAuthSignInWithPangolin": "Log in met Pangolin", + "subscriptionRequiredToUse": "Een abonnement is vereist om deze functie te gebruiken.", + "idpDisabled": "Identiteitsaanbieders zijn uitgeschakeld.", + "orgAuthPageDisabled": "Pagina voor organisatie-authenticatie is uitgeschakeld.", + "domainRestartedDescription": "Domeinverificatie met succes opnieuw gestart", "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", - "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug." } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index cd341974..ffe77bb8 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -168,6 +168,9 @@ "siteSelect": "Wybierz witrynę", "siteSearch": "Szukaj witryny", "siteNotFound": "Nie znaleziono witryny.", + "selectCountry": "Wybierz kraj", + "searchCountries": "Szukaj krajów...", + "noCountryFound": "Nie znaleziono kraju.", "siteSelectionDescription": "Ta strona zapewni połączenie z celem.", "resourceType": "Typ zasobu", "resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Połączono", "idpErrorConnectingTo": "Wystąpił problem z połączeniem z {name}. Skontaktuj się z administratorem.", "idpErrorNotFound": "Nie znaleziono IdP", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Nieprawidłowe zaproszenie", "inviteInvalidDescription": "Link zapraszający jest nieprawidłowy.", "inviteErrorWrongUser": "Zaproszenie nie jest dla tego użytkownika", @@ -1155,7 +1156,7 @@ "containerLabels": "Etykiety", "containerLabelsCount": "{count, plural, one {# etykieta} few {# etykiety} many {# etykiet} other {# etykiet}}", "containerLabelsTitle": "Etykiety kontenera", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Porty", "containerPortsMore": "+{count} więcej", "containerActions": "Akcje", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Subdomena: {subdomain}", "domainPickerNamespace": "Przestrzeń nazw: {namespace}", "domainPickerShowMore": "Pokaż więcej", + "regionSelectorTitle": "Wybierz region", + "regionSelectorInfo": "Wybór regionu pomaga nam zapewnić lepszą wydajność dla Twojej lokalizacji. Nie musisz być w tym samym regionie co Twój serwer.", + "regionSelectorPlaceholder": "Wybierz region", + "regionSelectorComingSoon": "Wkrótce dostępne", + "billingLoadingSubscription": "Ładowanie subskrypcji...", + "billingFreeTier": "Darmowy pakiet", + "billingWarningOverLimit": "Ostrzeżenie: Przekroczyłeś jeden lub więcej limitów użytkowania. Twoje witryny nie połączą się, dopóki nie zmienisz subskrypcji lub nie dostosujesz użytkowania.", + "billingUsageLimitsOverview": "Przegląd Limitów Użytkowania", + "billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@fossorial.io.", + "billingDataUsage": "Użycie danych", + "billingOnlineTime": "Czas Online Strony", + "billingUsers": "Aktywni użytkownicy", + "billingDomains": "Aktywne domeny", + "billingRemoteExitNodes": "Aktywne samodzielnie-hostowane węzły", + "billingNoLimitConfigured": "Nie skonfigurowano limitu", + "billingEstimatedPeriod": "Szacowany Okres Rozliczeniowy", + "billingIncludedUsage": "Zawarte użycie", + "billingIncludedUsageDescription": "Użycie zawarte w obecnym planie subskrypcji", + "billingFreeTierIncludedUsage": "Limity użycia dla darmowego pakietu", + "billingIncluded": "zawarte", + "billingEstimatedTotal": "Szacowana Całkowita:", + "billingNotes": "Notatki", + "billingEstimateNote": "To jest szacunkowe, oparte na Twoim obecnym użyciu.", + "billingActualChargesMayVary": "Rzeczywiste opłaty mogą się różnić.", + "billingBilledAtEnd": "Zostaniesz obciążony na koniec okresu rozliczeniowego.", + "billingModifySubscription": "Modyfikuj Subskrypcję", + "billingStartSubscription": "Rozpocznij Subskrypcję", + "billingRecurringCharge": "Opłata Cyklowa", + "billingManageSubscriptionSettings": "Zarządzaj ustawieniami i preferencjami subskrypcji", + "billingNoActiveSubscription": "Nie masz aktywnej subskrypcji. Rozpocznij subskrypcję, aby zwiększyć limity użytkowania.", + "billingFailedToLoadSubscription": "Nie udało się załadować subskrypcji", + "billingFailedToLoadUsage": "Nie udało się załadować użycia", + "billingFailedToGetCheckoutUrl": "Nie udało się uzyskać adresu URL zakupu", + "billingPleaseTryAgainLater": "Spróbuj ponownie później.", + "billingCheckoutError": "Błąd przy kasie", + "billingFailedToGetPortalUrl": "Nie udało się uzyskać adresu URL portalu", + "billingPortalError": "Błąd Portalu", + "billingDataUsageInfo": "Jesteś obciążony za wszystkie dane przesyłane przez bezpieczne tunele, gdy jesteś podłączony do chmury. Obejmuje to zarówno ruch przychodzący, jak i wychodzący we wszystkich Twoich witrynach. Gdy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie ograniczysz użycia. Dane nie będą naliczane przy użyciu węzłów.", + "billingOnlineTimeInfo": "Opłata zależy od tego, jak długo twoje strony pozostają połączone z chmurą. Na przykład 44,640 minut oznacza jedną stronę działającą 24/7 przez cały miesiąc. Kiedy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie zmniejsz jego wykorzystania. Czas nie będzie naliczany przy użyciu węzłów.", + "billingUsersInfo": "Jesteś obciążany za każdego użytkownika w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont użytkowników w twojej organizacji.", + "billingDomainInfo": "Jesteś obciążany za każdą domenę w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont domen w twojej organizacji.", + "billingRemoteExitNodesInfo": "Jesteś obciążany za każdy zarządzany węzeł w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych zarządzanych węzłów w twojej organizacji.", "domainNotFound": "Nie znaleziono domeny", "domainNotFoundDescription": "Zasób jest wyłączony, ponieważ domena nie istnieje już w naszym systemie. Proszę ustawić nową domenę dla tego zasobu.", "failed": "Niepowodzenie", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Zmiany DNS mogą zająć trochę czasu na rozpropagowanie się w Internecie. Może to potrwać od kilku minut do 48 godzin, w zależności od dostawcy DNS i ustawień TTL.", "resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP", "resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP", + "billingPricingCalculatorLink": "Kalkulator Cen", "signUpTerms": { "IAgreeToThe": "Zgadzam się z", "termsOfService": "warunkami usługi", @@ -1368,6 +1412,41 @@ "addNewTarget": "Dodaj nowy cel", "targetsList": "Lista celów", "targetErrorDuplicateTargetFound": "Znaleziono duplikat celu", + "healthCheckHealthy": "Zdrowy", + "healthCheckUnhealthy": "Niezdrowy", + "healthCheckUnknown": "Nieznany", + "healthCheck": "Kontrola Zdrowia", + "configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia", + "configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}", + "enableHealthChecks": "Włącz Kontrole Zdrowia", + "enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.", + "healthScheme": "Metoda", + "healthSelectScheme": "Wybierz metodę", + "healthCheckPath": "Ścieżka", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "Ścieżka do sprawdzania stanu zdrowia.", + "healthyIntervalSeconds": "Interwał Zdrowy", + "unhealthyIntervalSeconds": "Interwał Niezdrowy", + "IntervalSeconds": "Interwał Zdrowy", + "timeoutSeconds": "Limit Czasu", + "timeIsInSeconds": "Czas w sekundach", + "retryAttempts": "Próby Ponowienia", + "expectedResponseCodes": "Oczekiwane Kody Odpowiedzi", + "expectedResponseCodesDescription": "Kod statusu HTTP, który wskazuje zdrowy status. Jeśli pozostanie pusty, uznaje się 200-300 za zdrowy.", + "customHeaders": "Niestandardowe nagłówki", + "customHeadersDescription": "Nagłówki oddzielone: Nazwa nagłówka: wartość", + "headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.", + "saveHealthCheck": "Zapisz Kontrolę Zdrowia", + "healthCheckSaved": "Kontrola Zdrowia Zapisana", + "healthCheckSavedDescription": "Konfiguracja kontroli zdrowia została zapisana pomyślnie", + "healthCheckError": "Błąd Kontroli Zdrowia", + "healthCheckErrorDescription": "Wystąpił błąd podczas zapisywania konfiguracji kontroli zdrowia", + "healthCheckPathRequired": "Ścieżka kontroli zdrowia jest wymagana", + "healthCheckMethodRequired": "Metoda HTTP jest wymagana", + "healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund", + "healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę", + "healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1", "httpMethod": "Metoda HTTP", "selectHttpMethod": "Wybierz metodę HTTP", "domainPickerSubdomainLabel": "Poddomena", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Wprowadź poddomenę, aby wyszukać i wybrać z dostępnych darmowych domen.", "domainPickerFreeDomains": "Darmowe domeny", "domainPickerSearchForAvailableDomains": "Szukaj dostępnych domen", + "domainPickerNotWorkSelfHosted": "Uwaga: Darmowe domeny nie są obecnie dostępne dla instancji samodzielnie-hostowanych.", "resourceDomain": "Domena", "resourceEditDomain": "Edytuj domenę", "siteName": "Nazwa strony", @@ -1463,6 +1543,72 @@ "autoLoginError": "Błąd automatycznego logowania", "autoLoginErrorNoRedirectUrl": "Nie otrzymano URL przekierowania od dostawcy tożsamości.", "autoLoginErrorGeneratingUrl": "Nie udało się wygenerować URL uwierzytelniania.", + "remoteExitNodeManageRemoteExitNodes": "Zarządzaj Samodzielnie-Hostingowane", + "remoteExitNodeDescription": "Zarządzaj węzłami w celu rozszerzenia połączenia z siecią", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Szukaj węzłów...", + "remoteExitNodeAdd": "Dodaj węzeł", + "remoteExitNodeErrorDelete": "Błąd podczas usuwania węzła", + "remoteExitNodeQuestionRemove": "Czy na pewno chcesz usunąć węzeł {selectedNode} z organizacji?", + "remoteExitNodeMessageRemove": "Po usunięciu, węzeł nie będzie już dostępny.", + "remoteExitNodeMessageConfirm": "Aby potwierdzić, wpisz nazwę węzła poniżej.", + "remoteExitNodeConfirmDelete": "Potwierdź usunięcie węzła", + "remoteExitNodeDelete": "Usuń węzeł", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Utwórz węzeł", + "description": "Utwórz nowy węzeł, aby rozszerzyć połączenie z siecią", + "viewAllButton": "Zobacz wszystkie węzły", + "strategy": { + "title": "Strategia Tworzenia", + "description": "Wybierz to, aby ręcznie skonfigurować węzeł lub wygenerować nowe poświadczenia.", + "adopt": { + "title": "Zaadoptuj Węzeł", + "description": "Wybierz to, jeśli masz już dane logowania dla węzła." + }, + "generate": { + "title": "Generuj Klucze", + "description": "Wybierz to, jeśli chcesz wygenerować nowe klucze dla węzła" + } + }, + "adopt": { + "title": "Zaadoptuj Istniejący Węzeł", + "description": "Wprowadź dane logowania istniejącego węzła, który chcesz przyjąć", + "nodeIdLabel": "ID węzła", + "nodeIdDescription": "ID istniejącego węzła, który chcesz przyjąć", + "secretLabel": "Sekret", + "secretDescription": "Sekretny klucz istniejącego węzła", + "submitButton": "Przyjmij węzeł" + }, + "generate": { + "title": "Wygenerowane Poświadczenia", + "description": "Użyj tych danych logowania, aby skonfigurować węzeł", + "nodeIdTitle": "ID węzła", + "secretTitle": "Sekret", + "saveCredentialsTitle": "Dodaj Poświadczenia do Konfiguracji", + "saveCredentialsDescription": "Dodaj te poświadczenia do pliku konfiguracyjnego swojego samodzielnie-hostowanego węzła Pangolin, aby zakończyć połączenie.", + "submitButton": "Utwórz węzeł" + }, + "validation": { + "adoptRequired": "Identyfikator węzła i sekret są wymagane podczas przyjmowania istniejącego węzła" + }, + "errors": { + "loadDefaultsFailed": "Nie udało się załadować domyślnych ustawień", + "defaultsNotLoaded": "Domyślne ustawienia nie zostały załadowane", + "createFailed": "Nie udało się utworzyć węzła" + }, + "success": { + "created": "Węzeł utworzony pomyślnie" + } + }, + "remoteExitNodeSelection": "Wybór węzła", + "remoteExitNodeSelectionDescription": "Wybierz węzeł do przekierowania ruchu dla tej lokalnej witryny", + "remoteExitNodeRequired": "Węzeł musi być wybrany dla lokalnych witryn", + "noRemoteExitNodesAvailable": "Brak dostępnych węzłów", + "noRemoteExitNodesAvailableDescription": "Węzły nie są dostępne dla tej organizacji. Utwórz węzeł, aby używać lokalnych witryn.", + "exitNode": "Węzeł Wyjściowy", + "country": "Kraj", + "rulesMatchCountry": "Obecnie bazuje na adresie IP źródła", "managedSelfHosted": { "title": "Zarządzane Samodzielnie-Hostingowane", "description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Wykryto międzynarodową domenę", "willbestoredas": "Będą przechowywane jako:", + "roleMappingDescription": "Określ jak role są przypisywane do użytkowników podczas logowania się, gdy automatyczne świadczenie jest włączone.", + "selectRole": "Wybierz rolę", + "roleMappingExpression": "Wyrażenie", + "selectRolePlaceholder": "Wybierz rolę", + "selectRoleDescription": "Wybierz rolę do przypisania wszystkim użytkownikom od tego dostawcy tożsamości", + "roleMappingExpressionDescription": "Wprowadź wyrażenie JMESŚcieżki, aby wyodrębnić informacje o roli z tokenu ID", + "idpTenantIdRequired": "ID lokatora jest wymagane", + "invalidValue": "Nieprawidłowa wartość", + "idpTypeLabel": "Typ dostawcy tożsamości", + "roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'", + "idpGoogleConfiguration": "Konfiguracja Google", + "idpGoogleConfigurationDescription": "Skonfiguruj swoje poświadczenia Google OAuth2", + "idpGoogleClientIdDescription": "Twój identyfikator klienta Google OAuth2", + "idpGoogleClientSecretDescription": "Twój klucz klienta Google OAuth2", + "idpAzureConfiguration": "Konfiguracja Azure Entra ID", + "idpAzureConfigurationDescription": "Skonfiguruj swoje dane logowania OAuth2 Azure Entra", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "twoj-lokator", + "idpAzureTenantIdDescription": "Twój identyfikator dzierżawcy Azure (znaleziony w Podglądzie Azure Active Directory", + "idpAzureClientIdDescription": "Twój identyfikator klienta rejestracji aplikacji Azure", + "idpAzureClientSecretDescription": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Konfiguracja Google", + "idpAzureConfigurationTitle": "Konfiguracja Azure Entra ID", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Twój identyfikator klienta rejestracji aplikacji Azure", + "idpAzureClientSecretDescription2": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", "idpGoogleDescription": "Dostawca Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Niestandardowe nagłówki", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.", + "subnet": "Podsieć", + "subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.", + "authPage": "Strona uwierzytelniania", + "authPageDescription": "Skonfiguruj stronę uwierzytelniania dla swojej organizacji", + "authPageDomain": "Domena strony uwierzytelniania", + "noDomainSet": "Nie ustawiono domeny", + "changeDomain": "Zmień domenę", + "selectDomain": "Wybierz domenę", + "restartCertificate": "Uruchom ponownie certyfikat", + "editAuthPageDomain": "Edytuj domenę strony uwierzytelniania", + "setAuthPageDomain": "Ustaw domenę strony uwierzytelniania", + "failedToFetchCertificate": "Nie udało się pobrać certyfikatu", + "failedToRestartCertificate": "Nie udało się ponownie uruchomić certyfikatu", + "addDomainToEnableCustomAuthPages": "Dodaj domenę, aby włączyć niestandardowe strony uwierzytelniania dla Twojej organizacji", + "selectDomainForOrgAuthPage": "Wybierz domenę dla strony uwierzytelniania organizacji", "domainPickerProvidedDomain": "Dostarczona domena", "domainPickerFreeProvidedDomain": "Darmowa oferowana domena", "domainPickerVerified": "Zweryfikowano", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nie może być poprawne dla {domain}.", "domainPickerSubdomainSanitized": "Poddomena oczyszczona", "domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"", + "orgAuthSignInTitle": "Zaloguj się do swojej organizacji", + "orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować", + "orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.", + "orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin", + "subscriptionRequiredToUse": "Do korzystania z tej funkcji wymagana jest subskrypcja.", + "idpDisabled": "Dostawcy tożsamości są wyłączeni", + "orgAuthPageDisabled": "Strona autoryzacji organizacji jest wyłączona.", + "domainRestartedDescription": "Weryfikacja domeny zrestartowana pomyślnie", "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", - "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj." } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index a6667a9c..151ee73f 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -8,25 +8,25 @@ "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", - "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", - "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", + "componentsErrorNoMemberCreate": "Não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Não é atualmente um membro de nenhuma organização.", "welcome": "Bem-vindo ao Pangolin", "welcomeTo": "Bem-vindo ao", "componentsCreateOrg": "Criar uma organização", - "componentsMember": "Você é membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", + "componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", - "dismiss": "Descartar", + "dismiss": "Rejeitar", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", - "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", - "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", - "inviteLoginUser": "Verifique se você está logado como o usuário correto.", - "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que está a tentar aceder não é para este utilizador.", + "inviteLoginUser": "Verifique se você está logado como o utilizador correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que está a tentar aceder não é para um utilizador que existe.", "inviteCreateUser": "Por favor, crie uma conta primeiro.", - "goHome": "Ir para casa", - "inviteLogInOtherUser": "Fazer login como um usuário diferente", + "goHome": "Voltar ao inicio", + "inviteLogInOtherUser": "Fazer login como um utilizador diferente", "createAnAccount": "Crie uma conta", - "inviteNotAccepted": "Convite não aceito", + "inviteNotAccepted": "Convite não aceite", "authCreateAccount": "Crie uma conta para começar", "authNoAccount": "Não possui uma conta?", "email": "e-mail", @@ -34,23 +34,23 @@ "confirmPassword": "Confirmar senha", "createAccount": "Criar conta", "viewSettings": "Visualizar configurações", - "delete": "excluir", + "delete": "apagar", "name": "Nome:", "online": "Disponível", "offline": "Desconectado", "site": "site", - "dataIn": "Dados em", + "dataIn": "Dados de entrada", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", - "siteConfirmDelete": "Confirmar exclusão do site", + "siteConfirmDelete": "Confirmar que pretende apagar o site", "siteDelete": "Excluir site", "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", - "siteManageSites": "Gerenciar sites", + "siteManageSites": "Gerir sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", @@ -79,10 +79,10 @@ "operatingSystem": "Sistema operacional", "commands": "Comandos", "recommended": "Recomendados", - "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteNewtDescription": "Para a melhor experiência do utilizador, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", - "siteErrorDelete": "Erro ao excluir site", + "siteErrorDelete": "Erro ao apagar site", "siteErrorUpdate": "Falha ao atualizar site", "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", "siteUpdated": "Site atualizado", @@ -105,12 +105,12 @@ "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", "siteInfo": "Informações do Site", "status": "SItuação", - "shareTitle": "Gerenciar links de compartilhamento", + "shareTitle": "Gerir links partilhados", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", "shareCreate": "Criar Link de Compartilhamento", - "shareErrorDelete": "Falha ao excluir o link", - "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", + "shareErrorDelete": "Falha ao apagar o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao apagar o link", "shareDeleted": "Link excluído", "shareDeletedDescription": "O link foi eliminado", "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", @@ -127,13 +127,13 @@ "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", "shareErrorCreate": "Falha ao criar link de compartilhamento", "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", - "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", + "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", "shareTitleOptional": "Título (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", - "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", - "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", + "shareAccessHint": "Qualquer um com este link pode aceder o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", "resourcesNotFound": "Nenhum recurso encontrado", @@ -145,11 +145,11 @@ "expires": "Expira", "never": "nunca", "shareErrorSelectResource": "Por favor, selecione um recurso", - "resourceTitle": "Gerenciar Recursos", + "resourceTitle": "Gerir Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", - "resourceErrorDelte": "Erro ao excluir recurso", + "resourceErrorDelte": "Erro ao apagar recurso", "authentication": "Autenticação", "protected": "Protegido", "notProtected": "Não Protegido", @@ -168,9 +168,12 @@ "siteSelect": "Selecionar site", "siteSearch": "Procurar no site", "siteNotFound": "Nenhum site encontrado.", + "selectCountry": "Selecionar país", + "searchCountries": "Buscar países...", + "noCountryFound": "Nenhum país encontrado.", "siteSelectionDescription": "Este site fornecerá conectividade ao destino.", "resourceType": "Tipo de Recurso", - "resourceTypeDescription": "Determine como você deseja acessar seu recurso", + "resourceTypeDescription": "Determine como você deseja aceder seu recurso", "resourceHTTPSSettings": "Configurações de HTTPS", "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", "domainType": "Tipo de domínio", @@ -192,7 +195,7 @@ "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", "resourceDelete": "Excluir Recurso", - "resourceDeleteConfirm": "Confirmar exclusão de recurso", + "resourceDeleteConfirm": "Confirmar que pretende apagar o recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -208,14 +211,14 @@ "passToAuth": "Passar para Autenticação", "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", - "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Salvar configurações gerais", - "saveSettings": "Salvar Configurações", + "orgGeneralSettingsDescription": "Gerir os detalhes e a configuração da sua organização", + "saveGeneralSettings": "Guardar configurações gerais", + "saveSettings": "Guardar Configurações", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", - "orgDeleteConfirm": "Confirmar exclusão da organização", - "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", + "orgDeleteConfirm": "Confirmar que pretende apagar a organização", + "orgMessageRemove": "Esta ação é irreversível e apagará todos os dados associados.", "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", "orgUpdated": "Organização atualizada", @@ -224,29 +227,29 @@ "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", "orgErrorFetch": "Falha ao buscar organizações", "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", - "orgErrorDelete": "Falha ao excluir organização", - "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", + "orgErrorDelete": "Falha ao apagar organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgMissing": "ID da Organização Ausente", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", - "accessUsersManage": "Gerenciar Usuários", - "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", - "accessUsersSearch": "Procurar usuários...", + "accessUsersManage": "Gerir Utilizadores", + "accessUsersDescription": "Convidar utilizadores e adicioná-los a funções para gerir o acesso à sua organização", + "accessUsersSearch": "Procurar utilizadores...", "accessUserCreate": "Criar Usuário", - "accessUserRemove": "Remover usuário", + "accessUserRemove": "Remover utilizador", "username": "Usuário:", "identityProvider": "Provedor de Identidade", "role": "Funções", "nameRequired": "O nome é obrigatório", - "accessRolesManage": "Gerenciar Funções", - "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", + "accessRolesManage": "Gerir Funções", + "accessRolesDescription": "Configurar funções para gerir o acesso à sua organização", "accessRolesSearch": "Pesquisar funções...", "accessRolesAdd": "Adicionar função", "accessRoleDelete": "Excluir Papel", "description": "Descrição:", "inviteTitle": "Convites Abertos", - "inviteDescription": "Gerencie seus convites para outros usuários", + "inviteDescription": "Gerir seus convites para outros utilizadores", "inviteSearch": "Procurar convites...", "minutes": "minutos", "hours": "horas", @@ -264,7 +267,7 @@ "apiKeysGeneralSettings": "Permissões", "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", "apiKeysList": "Sua Chave API", - "apiKeysSave": "Salvar Sua Chave API", + "apiKeysSave": "Guardar Sua Chave API", "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", "apiKeysInfo": "Sua chave API é:", "apiKeysConfirmCopy": "Eu copiei a chave API", @@ -277,33 +280,33 @@ "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", "apiKeysPermissionsGeneralSettings": "Permissões", "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", - "apiKeysPermissionsSave": "Salvar Permissões", + "apiKeysPermissionsSave": "Guardar Permissões", "apiKeysPermissionsTitle": "Permissões", "apiKeys": "Chaves API", "searchApiKeys": "Pesquisar chaves API...", "apiKeysAdd": "Gerar Chave API", - "apiKeysErrorDelete": "Erro ao excluir chave API", - "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", + "apiKeysErrorDelete": "Erro ao apagar chave API", + "apiKeysErrorDeleteMessage": "Erro ao apagar chave API", "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", - "apiKeysManage": "Gerenciar Chaves API", + "apiKeysManage": "Gerir Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", "apiKeysSettings": "Configurações de {apiKeyName}", - "userTitle": "Gerenciar Todos os Usuários", - "userDescription": "Visualizar e gerenciar todos os usuários no sistema", + "userTitle": "Gerir Todos os Utilizadores", + "userDescription": "Visualizar e gerir todos os utilizadores no sistema", "userAbount": "Sobre a Gestão de Usuário", - "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", - "userServer": "Usuários do Servidor", - "userSearch": "Pesquisar usuários do servidor...", - "userErrorDelete": "Erro ao excluir usuário", + "userAbountDescription": "Esta tabela exibe todos os objetos root do utilizador. Cada utilizador pode pertencer a várias organizações. Remover um utilizador de uma organização não exclui seu objeto de utilizador raiz - ele permanecerá no sistema. Para remover completamente um utilizador do sistema, você deve apagar seu objeto raiz usando a ação de apagar nesta tabela.", + "userServer": "Utilizadores do Servidor", + "userSearch": "Pesquisar utilizadores do servidor...", + "userErrorDelete": "Erro ao apagar utilizador", "userDeleteConfirm": "Confirmar Exclusão do Usuário", - "userDeleteServer": "Excluir usuário do servidor", - "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", - "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", - "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", + "userDeleteServer": "Excluir utilizador do servidor", + "userMessageRemove": "O utilizador será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do utilizador abaixo.", + "userQuestionRemove": "Tem certeza que deseja apagar o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", "valid": "Válido", "numberOfSites": "Número de sites", @@ -314,8 +317,8 @@ "licenseTermsAgree": "Você deve concordar com os termos da licença", "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", - "licenseErrorKeyDelete": "Falha ao excluir chave de licença", - "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", + "licenseErrorKeyDelete": "Falha ao apagar chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao apagar a chave de licença.", "licenseKeyDeleted": "Chave da licença excluída", "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", @@ -336,13 +339,13 @@ "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", - "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", + "licenseQuestionRemove": "Tem certeza que deseja apagar a chave de licença {selectedKey}?", "licenseKeyDelete": "Excluir Chave de Licença", - "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", - "licenseTitle": "Gerenciar Status da Licença", - "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", + "licenseKeyDeleteConfirm": "Confirmar que pretende apagar a chave de licença", + "licenseTitle": "Gerir Status da Licença", + "licenseTitleDescription": "Visualizar e gerir chaves de licença no sistema", "licenseHost": "Licença do host", - "licenseHostDescription": "Gerenciar a chave de licença principal do host.", + "licenseHostDescription": "Gerir a chave de licença principal do host.", "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", @@ -370,37 +373,37 @@ "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", - "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o utilizador novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", "inviteRemoveConfirm": "Confirmar Remoção do Convite", "inviteRegenerated": "Convite Regenerado", "inviteSent": "Um novo convite foi enviado para {email}.", - "inviteSentEmail": "Enviar notificação por e-mail ao usuário", + "inviteSentEmail": "Enviar notificação por e-mail ao utilizador", "inviteGenerate": "Um novo convite foi gerado para {email}.", "inviteDuplicateError": "Convite Duplicado", - "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", + "inviteDuplicateErrorDescription": "Já existe um convite para este utilizador.", "inviteRateLimitError": "Limite de Taxa Excedido", - "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRateLimitErrorDescription": "Excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", "inviteRegenerateError": "Falha ao Regenerar Convite", "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", "inviteValidityPeriod": "Período de Validade", "inviteValidityPeriodSelect": "Selecione o período de validade", - "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", + "inviteRegenerateMessage": "O convite foi regenerado. O utilizador deve aceder o link abaixo para aceitar o convite.", "inviteRegenerateButton": "Regenerar", "expiresAt": "Expira em", "accessRoleUnknown": "Função Desconhecida", "placeholder": "Espaço reservado", - "userErrorOrgRemove": "Falha ao remover usuário", - "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", + "userErrorOrgRemove": "Falha ao remover utilizador", + "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o utilizador.", "userOrgRemoved": "Usuário removido", - "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", + "userOrgRemovedDescription": "O utilizador {email} foi removido da organização.", "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", - "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", - "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", + "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", + "userMessageOrgConfirm": "Para confirmar, digite o nome do utilizador abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrg": "Remover Usuário da Organização", - "users": "Usuários", + "users": "Utilizadores", "accessRoleMember": "Membro", "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", @@ -408,7 +411,7 @@ "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", - "usernameRequired": "Nome de usuário é obrigatório", + "usernameRequired": "Nome de utilizador é obrigatório", "idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "accessRoleErrorFetch": "Falha ao buscar funções", @@ -416,51 +419,51 @@ "idpErrorFetch": "Falha ao buscar provedores de identidade", "idpErrorFetchDescription": "Ocorreu um erro ao buscar provedores de identidade", "userErrorExists": "Usuário já existe", - "userErrorExistsDescription": "Este usuário já é membro da organização.", - "inviteError": "Falha ao convidar usuário", - "inviteErrorDescription": "Ocorreu um erro ao convidar o usuário", + "userErrorExistsDescription": "Este utilizador já é membro da organização.", + "inviteError": "Falha ao convidar utilizador", + "inviteErrorDescription": "Ocorreu um erro ao convidar o utilizador", "userInvited": "Usuário convidado", - "userInvitedDescription": "O usuário foi convidado com sucesso.", - "userErrorCreate": "Falha ao criar usuário", - "userErrorCreateDescription": "Ocorreu um erro ao criar o usuário", + "userInvitedDescription": "O utilizador foi convidado com sucesso.", + "userErrorCreate": "Falha ao criar utilizador", + "userErrorCreateDescription": "Ocorreu um erro ao criar o utilizador", "userCreated": "Usuário criado", - "userCreatedDescription": "O usuário foi criado com sucesso.", + "userCreatedDescription": "O utilizador foi criado com sucesso.", "userTypeInternal": "Usuário Interno", - "userTypeInternalDescription": "Convidar um usuário para se juntar à sua organização diretamente.", + "userTypeInternalDescription": "Convidar um utilizador para se juntar à sua organização diretamente.", "userTypeExternal": "Usuário Externo", - "userTypeExternalDescription": "Criar um usuário com um provedor de identidade externo.", - "accessUserCreateDescription": "Siga os passos abaixo para criar um novo usuário", - "userSeeAll": "Ver Todos os Usuários", + "userTypeExternalDescription": "Criar um utilizador com um provedor de identidade externo.", + "accessUserCreateDescription": "Siga os passos abaixo para criar um novo utilizador", + "userSeeAll": "Ver Todos os Utilizadores", "userTypeTitle": "Tipo de Usuário", - "userTypeDescription": "Determine como você deseja criar o usuário", + "userTypeDescription": "Determine como você deseja criar o utilizador", "userSettings": "Informações do Usuário", - "userSettingsDescription": "Insira os detalhes para o novo usuário", - "inviteEmailSent": "Enviar e-mail de convite para o usuário", + "userSettingsDescription": "Insira os detalhes para o novo utilizador", + "inviteEmailSent": "Enviar e-mail de convite para o utilizador", "inviteValid": "Válido Por", "selectDuration": "Selecionar duração", "accessRoleSelect": "Selecionar função", - "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", - "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", + "inviteEmailSentDescription": "Um e-mail foi enviado ao utilizador com o link de acesso abaixo. Eles devem aceder ao link para aceitar o convite.", + "inviteSentDescription": "O utilizador foi convidado. Eles devem aceder ao link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, one {# dia} other {# dias}}.", "idpTitle": "Informações Gerais", - "idpSelect": "Selecione o provedor de identidade para o usuário externo", - "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", - "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", + "idpSelect": "Selecione o provedor de identidade para o utilizador externo", + "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar utilizadores externos.", + "usernameUniq": "Isto deve corresponder ao nome de utilizador único que existe no provedor de identidade selecionado.", "emailOptional": "E-mail (Opcional)", "nameOptional": "Nome (Opcional)", - "accessControls": "Controles de Acesso", - "userDescription2": "Gerenciar as configurações deste usuário", - "accessRoleErrorAdd": "Falha ao adicionar usuário à função", - "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar usuário à função.", + "accessControls": "Controlos de Acesso", + "userDescription2": "Gerir as configurações deste utilizador", + "accessRoleErrorAdd": "Falha ao adicionar utilizador à função", + "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar utilizador à função.", "userSaved": "Usuário salvo", - "userSavedDescription": "O usuário foi atualizado.", + "userSavedDescription": "O utilizador foi atualizado.", "autoProvisioned": "Auto provisionado", - "autoProvisionedDescription": "Permitir que este usuário seja gerenciado automaticamente pelo provedor de identidade", - "accessControlsDescription": "Gerencie o que este usuário pode acessar e fazer na organização", - "accessControlsSubmit": "Salvar Controles de Acesso", + "autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade", + "accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização", + "accessControlsSubmit": "Guardar Controlos de Acesso", "roles": "Funções", - "accessUsersRoles": "Gerenciar Usuários e Funções", - "accessUsersRolesDescription": "Convide usuários e adicione-os a funções para gerenciar o acesso à sua organização", + "accessUsersRoles": "Gerir Utilizadores e Funções", + "accessUsersRolesDescription": "Convide utilizadores e adicione-os a funções para gerir o acesso à sua organização", "key": "Chave", "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", @@ -494,7 +497,7 @@ "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", - "targetTlsSubmit": "Salvar Configurações", + "targetTlsSubmit": "Guardar Configurações", "targets": "Configuração de Alvos", "targetsDescription": "Configure alvos para rotear tráfego para seus serviços de backend", "targetStickySessions": "Ativar Sessões Persistentes", @@ -503,12 +506,12 @@ "targetSubmit": "Adicionar Alvo", "targetNoOne": "Sem alvos. Adicione um alvo usando o formulário.", "targetNoOneDescription": "Adicionar mais de um alvo acima habilitará o balanceamento de carga.", - "targetsSubmit": "Salvar Alvos", + "targetsSubmit": "Guardar Alvos", "proxyAdditional": "Configurações Adicionais de Proxy", "proxyAdditionalDescription": "Configure como seu recurso lida com configurações de proxy", "proxyCustomHeader": "Cabeçalho Host Personalizado", "proxyCustomHeaderDescription": "O cabeçalho host para definir ao fazer proxy de requisições. Deixe vazio para usar o padrão.", - "proxyAdditionalSubmit": "Salvar Configurações de Proxy", + "proxyAdditionalSubmit": "Guardar Configurações de Proxy", "subnetMaskErrorInvalid": "Máscara de subnet inválida. Deve estar entre 0 e 32.", "ipAddressErrorInvalidFormat": "Formato de endereço IP inválido", "ipAddressErrorInvalidOctet": "Octeto de endereço IP inválido", @@ -561,7 +564,7 @@ "ruleSubmit": "Adicionar Regra", "rulesNoOne": "Sem regras. Adicione uma regra usando o formulário.", "rulesOrder": "As regras são avaliadas por prioridade em ordem ascendente.", - "rulesSubmit": "Salvar Regras", + "rulesSubmit": "Guardar Regras", "resourceErrorCreate": "Erro ao criar recurso", "resourceErrorCreateDescription": "Ocorreu um erro ao criar o recurso", "resourceErrorCreateMessage": "Erro ao criar recurso:", @@ -576,7 +579,7 @@ "resourcesDescription": "Recursos são proxies para aplicações executando em sua rede privada. Crie um recurso para qualquer serviço HTTP/HTTPS ou TCP/UDP bruto em sua rede privada. Cada recurso deve estar conectado a um site para habilitar conectividade privada e segura através de um túnel WireGuard criptografado.", "resourcesWireGuardConnect": "Conectividade segura com criptografia WireGuard", "resourcesMultipleAuthenticationMethods": "Configure múltiplos métodos de autenticação", - "resourcesUsersRolesAccess": "Controle de acesso baseado em usuários e funções", + "resourcesUsersRolesAccess": "Controle de acesso baseado em utilizadores e funções", "resourcesErrorUpdate": "Falha ao alternar recurso", "resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso", "access": "Acesso", @@ -606,7 +609,7 @@ "pangolinSettings": "Configurações - Pangolin", "accessRoleYour": "Sua função:", "accessRoleSelect2": "Selecionar uma função", - "accessUserSelect": "Selecionar um usuário", + "accessUserSelect": "Selecionar um utilizador", "otpEmailEnter": "Digite um e-mail", "otpEmailEnterDescription": "Pressione enter para adicionar um e-mail após digitá-lo no campo de entrada.", "otpEmailErrorInvalid": "Endereço de e-mail inválido. O caractere curinga (*) deve ser a parte local inteira.", @@ -616,8 +619,8 @@ "otpEmailTitleDescription": "Requer autenticação baseada em e-mail para acesso ao recurso", "otpEmailWhitelist": "Lista de E-mails Permitidos", "otpEmailWhitelistList": "E-mails na Lista Permitida", - "otpEmailWhitelistListDescription": "Apenas usuários com estes endereços de e-mail poderão acessar este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", - "otpEmailWhitelistSave": "Salvar Lista Permitida", + "otpEmailWhitelistListDescription": "Apenas utilizadores com estes endereços de e-mail poderão aceder este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", + "otpEmailWhitelistSave": "Guardar Lista Permitida", "passwordAdd": "Adicionar Senha", "passwordRemove": "Remover Senha", "pincodeAdd": "Adicionar Código PIN", @@ -657,14 +660,14 @@ "resourcePincodeSetupDescription": "O código PIN do recurso foi definido com sucesso", "resourcePincodeSetupTitle": "Definir Código PIN", "resourcePincodeSetupTitleDescription": "Defina um código PIN para proteger este recurso", - "resourceRoleDescription": "Administradores sempre podem acessar este recurso.", - "resourceUsersRoles": "Usuários e Funções", - "resourceUsersRolesDescription": "Configure quais usuários e funções podem visitar este recurso", - "resourceUsersRolesSubmit": "Salvar Usuários e Funções", + "resourceRoleDescription": "Administradores sempre podem aceder este recurso.", + "resourceUsersRoles": "Utilizadores e Funções", + "resourceUsersRolesDescription": "Configure quais utilizadores e funções podem visitar este recurso", + "resourceUsersRolesSubmit": "Guardar Utilizadores e Funções", "resourceWhitelistSave": "Salvo com sucesso", "resourceWhitelistSaveDescription": "As configurações da lista permitida foram salvas", "ssoUse": "Usar SSO da Plataforma", - "ssoUseDescription": "Os usuários existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", + "ssoUseDescription": "Os utilizadores existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", "proxyErrorInvalidPort": "Número da porta inválido", "subdomainErrorInvalid": "Subdomínio inválido", "domainErrorFetch": "Erro ao buscar domínios", @@ -690,7 +693,7 @@ "siteDestination": "Site de Destino", "searchSites": "Pesquisar sites", "accessRoleCreate": "Criar Função", - "accessRoleCreateDescription": "Crie uma nova função para agrupar usuários e gerenciar suas permissões.", + "accessRoleCreateDescription": "Crie uma nova função para agrupar utilizadores e gerir suas permissões.", "accessRoleCreateSubmit": "Criar Função", "accessRoleCreated": "Função criada", "accessRoleCreatedDescription": "A função foi criada com sucesso.", @@ -700,13 +703,13 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a apagar a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", "accessRoleRemoved": "Função removida", "accessRoleRemovedDescription": "A função foi removida com sucesso.", - "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", + "accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", @@ -914,12 +917,10 @@ "idpConnectingToFinished": "Conectado", "idpErrorConnectingTo": "Ocorreu um problema ao ligar a {name}. Por favor, contacte o seu administrador.", "idpErrorNotFound": "IdP não encontrado", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", - "inviteErrorWrongUser": "O convite não é para este usuário", - "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", + "inviteErrorWrongUser": "O convite não é para este utilizador", + "inviteErrorUserNotExists": "O utilizador não existe. Por favor, crie uma conta primeiro.", "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", "inviteErrorExpired": "O convite pode ter expirado", "inviteErrorRevoked": "O convite pode ter sido revogado", @@ -934,7 +935,7 @@ "home": "Início", "accessControl": "Controle de Acesso", "settings": "Configurações", - "usersAll": "Todos os Usuários", + "usersAll": "Todos os Utilizadores", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", "noResults": "Nenhum resultado encontrado.", @@ -987,8 +988,8 @@ "licenseTierProfessionalRequired": "Edição Profissional Necessária", "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", "actionGetOrg": "Obter Organização", - "updateOrgUser": "Atualizar usuário Org", - "createOrgUser": "Criar usuário Org", + "updateOrgUser": "Atualizar utilizador Org", + "createOrgUser": "Criar utilizador Org", "actionUpdateOrg": "Atualizar Organização", "actionUpdateUser": "Atualizar Usuário", "actionGetUser": "Obter Usuário", @@ -1135,8 +1136,8 @@ "sidebarRoles": "Papéis", "sidebarShareableLinks": "Links compartilháveis", "sidebarApiKeys": "Chaves API", - "sidebarSettings": "Confirgurações", - "sidebarAllUsers": "Todos os usuários", + "sidebarSettings": "Configurações", + "sidebarAllUsers": "Todos os utilizadores", "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "sidebarClients": "Clientes (Beta)", @@ -1189,7 +1190,7 @@ "loading": "Carregando", "restart": "Reiniciar", "domains": "Domínios", - "domainsDescription": "Gerencie domínios para sua organização", + "domainsDescription": "Gerir domínios para sua organização", "domainsSearch": "Pesquisar domínios...", "domainAdd": "Adicionar Domínio", "domainAddDescription": "Registre um novo domínio com sua organização", @@ -1217,7 +1218,7 @@ "pending": "Pendente", "sidebarBilling": "Faturamento", "billing": "Faturamento", - "orgBillingDescription": "Gerencie suas informações de faturamento e assinaturas", + "orgBillingDescription": "Gerir suas informações de faturação e assinaturas", "github": "GitHub", "pangolinHosted": "Hospedagem Pangolin", "fossorial": "Fossorial", @@ -1232,7 +1233,7 @@ "completeSetup": "Configuração Completa", "accountSetupSuccess": "Configuração da conta concluída! Bem-vindo ao Pangolin!", "documentation": "Documentação", - "saveAllSettings": "Salvar Todas as Configurações", + "saveAllSettings": "Guardar Todas as Configurações", "settingsUpdated": "Configurações atualizadas", "settingsUpdatedDescription": "Todas as configurações foram atualizadas com sucesso", "settingsErrorUpdate": "Falha ao atualizar configurações", @@ -1257,13 +1258,55 @@ "domainPickerSubdomain": "Subdomínio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostrar Mais", + "regionSelectorTitle": "Selecionar Região", + "regionSelectorInfo": "Selecionar uma região nos ajuda a fornecer melhor desempenho para sua localização. Você não precisa estar na mesma região que seu servidor.", + "regionSelectorPlaceholder": "Escolher uma região", + "regionSelectorComingSoon": "Em breve", + "billingLoadingSubscription": "Carregando assinatura...", + "billingFreeTier": "Plano Gratuito", + "billingWarningOverLimit": "Aviso: Você ultrapassou um ou mais limites de uso. Seus sites não se conectarão até você modificar sua assinatura ou ajustar seu uso.", + "billingUsageLimitsOverview": "Visão Geral dos Limites de Uso", + "billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@fossorial.io.", + "billingDataUsage": "Uso de Dados", + "billingOnlineTime": "Tempo Online do Site", + "billingUsers": "Usuários Ativos", + "billingDomains": "Domínios Ativos", + "billingRemoteExitNodes": "Nodos Auto-Hospedados Ativos", + "billingNoLimitConfigured": "Nenhum limite configurado", + "billingEstimatedPeriod": "Período Estimado de Cobrança", + "billingIncludedUsage": "Uso Incluído", + "billingIncludedUsageDescription": "Uso incluído no seu plano de assinatura atual", + "billingFreeTierIncludedUsage": "Limites de uso do plano gratuito", + "billingIncluded": "incluído", + "billingEstimatedTotal": "Total Estimado:", + "billingNotes": "Notas", + "billingEstimateNote": "Esta é uma estimativa baseada no seu uso atual.", + "billingActualChargesMayVary": "As cobranças reais podem variar.", + "billingBilledAtEnd": "Sua cobrança será feita ao final do período de cobrança.", + "billingModifySubscription": "Modificar Assinatura", + "billingStartSubscription": "Iniciar Assinatura", + "billingRecurringCharge": "Cobrança Recorrente", + "billingManageSubscriptionSettings": "Gerenciar as configurações e preferências da sua assinatura", + "billingNoActiveSubscription": "Você não tem uma assinatura ativa. Inicie sua assinatura para aumentar os limites de uso.", + "billingFailedToLoadSubscription": "Falha ao carregar assinatura", + "billingFailedToLoadUsage": "Falha ao carregar uso", + "billingFailedToGetCheckoutUrl": "Falha ao obter URL de checkout", + "billingPleaseTryAgainLater": "Por favor, tente novamente mais tarde.", + "billingCheckoutError": "Erro de Checkout", + "billingFailedToGetPortalUrl": "Falha ao obter URL do portal", + "billingPortalError": "Erro do Portal", + "billingDataUsageInfo": "Você é cobrado por todos os dados transferidos através de seus túneis seguros quando conectado à nuvem. Isso inclui o tráfego de entrada e saída em todos os seus sites. Quando você atingir o seu limite, seus sites desconectarão até que você atualize seu plano ou reduza o uso. Os dados não serão cobrados ao usar os nós.", + "billingOnlineTimeInfo": "Cobrança de acordo com o tempo em que seus sites permanecem conectados à nuvem. Por exemplo, 44,640 minutos é igual a um site que roda 24/7 para um mês inteiro. Quando você atinge o seu limite, seus sites desconectarão até que você faça o upgrade do seu plano ou reduza o uso. O tempo não é cobrado ao usar nós.", + "billingUsersInfo": "Você será cobrado por cada usuário em sua organização. A cobrança é calculada diariamente com base no número de contas de usuário ativas em sua organização.", + "billingDomainInfo": "Você será cobrado por cada domínio em sua organização. A cobrança é calculada diariamente com base no número de contas de domínio ativas em sua organização.", + "billingRemoteExitNodesInfo": "Você será cobrado por cada Nodo gerenciado em sua organização. A cobrança é calculada diariamente com base no número de Nodos gerenciados ativos em sua organização.", "domainNotFound": "Domínio Não Encontrado", "domainNotFoundDescription": "Este recurso está desativado porque o domínio não existe mais em nosso sistema. Defina um novo domínio para este recurso.", "failed": "Falhou", "createNewOrgDescription": "Crie uma nova organização", "organization": "Organização", "port": "Porta", - "securityKeyManage": "Gerenciar chaves de segurança", + "securityKeyManage": "Gerir chaves de segurança", "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", "securityKeyRegister": "Registrar nova chave de segurança", "securityKeyList": "Suas chaves de segurança", @@ -1314,12 +1357,13 @@ "createDomainARecords": "Registros A", "createDomainRecordNumber": "Registrar {number}", "createDomainTxtRecords": "Registros TXT", - "createDomainSaveTheseRecords": "Salvar Esses Registros", + "createDomainSaveTheseRecords": "Guardar Esses Registros", "createDomainSaveTheseRecordsDescription": "Certifique-se de salvar esses registros DNS, pois você não os verá novamente.", "createDomainDnsPropagation": "Propagação DNS", "createDomainDnsPropagationDescription": "Alterações no DNS podem levar algum tempo para se propagar pela internet. Pode levar de alguns minutos a 48 horas, dependendo do seu provedor de DNS e das configurações de TTL.", "resourcePortRequired": "Número da porta é obrigatório para recursos não-HTTP", "resourcePortNotAllowed": "Número da porta não deve ser definido para recursos HTTP", + "billingPricingCalculatorLink": "Calculadora de Preços", "signUpTerms": { "IAgreeToThe": "Concordo com", "termsOfService": "os termos de serviço", @@ -1368,6 +1412,41 @@ "addNewTarget": "Adicionar Novo Alvo", "targetsList": "Lista de Alvos", "targetErrorDuplicateTargetFound": "Alvo duplicado encontrado", + "healthCheckHealthy": "Saudável", + "healthCheckUnhealthy": "Não Saudável", + "healthCheckUnknown": "Desconhecido", + "healthCheck": "Verificação de Saúde", + "configureHealthCheck": "Configurar Verificação de Saúde", + "configureHealthCheckDescription": "Configure a monitorização de saúde para {target}", + "enableHealthChecks": "Ativar Verificações de Saúde", + "enableHealthChecksDescription": "Monitore a saúde deste alvo. Você pode monitorar um ponto de extremidade diferente do alvo, se necessário.", + "healthScheme": "Método", + "healthSelectScheme": "Selecione o Método", + "healthCheckPath": "Caminho", + "healthHostname": "IP / Host", + "healthPort": "Porta", + "healthCheckPathDescription": "O caminho para verificar o estado de saúde.", + "healthyIntervalSeconds": "Intervalo Saudável", + "unhealthyIntervalSeconds": "Intervalo Não Saudável", + "IntervalSeconds": "Intervalo Saudável", + "timeoutSeconds": "Tempo Limite", + "timeIsInSeconds": "O tempo está em segundos", + "retryAttempts": "Tentativas de Repetição", + "expectedResponseCodes": "Códigos de Resposta Esperados", + "expectedResponseCodesDescription": "Código de status HTTP que indica estado saudável. Se deixado em branco, 200-300 é considerado saudável.", + "customHeaders": "Cabeçalhos Personalizados", + "customHeadersDescription": "Separados por cabeçalhos da nova linha: Nome do Cabeçalho: valor", + "headersValidationError": "Cabeçalhos devem estar no formato: Nome do Cabeçalho: valor.", + "saveHealthCheck": "Salvar Verificação de Saúde", + "healthCheckSaved": "Verificação de Saúde Salva", + "healthCheckSavedDescription": "Configuração de verificação de saúde salva com sucesso", + "healthCheckError": "Erro de Verificação de Saúde", + "healthCheckErrorDescription": "Ocorreu um erro ao salvar a configuração de verificação de saúde", + "healthCheckPathRequired": "O caminho de verificação de saúde é obrigatório", + "healthCheckMethodRequired": "O método HTTP é obrigatório", + "healthCheckIntervalMin": "O intervalo de verificação deve ser de pelo menos 5 segundos", + "healthCheckTimeoutMin": "O tempo limite deve ser de pelo menos 1 segundo", + "healthCheckRetryMin": "As tentativas de repetição devem ser pelo menos 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Selecionar método HTTP", "domainPickerSubdomainLabel": "Subdomínio", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Digite um subdomínio para buscar e selecionar entre os domínios gratuitos disponíveis.", "domainPickerFreeDomains": "Domínios Gratuitos", "domainPickerSearchForAvailableDomains": "Pesquise por domínios disponíveis", + "domainPickerNotWorkSelfHosted": "Nota: Domínios gratuitos fornecidos não estão disponíveis para instâncias auto-hospedadas no momento.", "resourceDomain": "Domínio", "resourceEditDomain": "Editar Domínio", "siteName": "Nome do Site", @@ -1401,7 +1481,7 @@ "editInternalResourceDialogSitePort": "Porta do Site", "editInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "editInternalResourceDialogCancel": "Cancelar", - "editInternalResourceDialogSaveResource": "Salvar Recurso", + "editInternalResourceDialogSaveResource": "Guardar Recurso", "editInternalResourceDialogSuccess": "Sucesso", "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno atualizado com sucesso", "editInternalResourceDialogError": "Erro", @@ -1428,7 +1508,7 @@ "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", "createInternalResourceDialogSitePort": "Porta do Site", - "createInternalResourceDialogSitePortDescription": "Use esta porta para acessar o recurso no site quando conectado com um cliente.", + "createInternalResourceDialogSitePortDescription": "Use esta porta para aceder o recurso no site quando conectado com um cliente.", "createInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "createInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "createInternalResourceDialogDestinationPortDescription": "A porta no IP de destino onde o recurso está acessível.", @@ -1452,7 +1532,7 @@ "siteAddress": "Endereço do Site", "siteAddressDescription": "Especificar o endereço IP do host para que os clientes se conectem. Este é o endereço interno do site na rede Pangolin para os clientes endereçarem. Deve estar dentro da sub-rede da Organização.", "autoLoginExternalIdp": "Login Automático com IDP Externo", - "autoLoginExternalIdpDescription": "Redirecionar imediatamente o usuário para o IDP externo para autenticação.", + "autoLoginExternalIdpDescription": "Redirecionar imediatamente o utilizador para o IDP externo para autenticação.", "selectIdp": "Selecionar IDP", "selectIdpPlaceholder": "Escolher um IDP...", "selectIdpRequired": "Por favor, selecione um IDP quando o login automático estiver ativado.", @@ -1463,6 +1543,72 @@ "autoLoginError": "Erro de Login Automático", "autoLoginErrorNoRedirectUrl": "Nenhum URL de redirecionamento recebido do provedor de identidade.", "autoLoginErrorGeneratingUrl": "Falha ao gerar URL de autenticação.", + "remoteExitNodeManageRemoteExitNodes": "Gerenciar Auto-Hospedados", + "remoteExitNodeDescription": "Gerencie os nós para estender sua conectividade de rede", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Buscar nós...", + "remoteExitNodeAdd": "Adicionar node", + "remoteExitNodeErrorDelete": "Erro ao excluir nó", + "remoteExitNodeQuestionRemove": "Tem certeza que deseja remover o nó {selectedNode} da organização?", + "remoteExitNodeMessageRemove": "Uma vez removido, o nó não estará mais acessível.", + "remoteExitNodeMessageConfirm": "Para confirmar, por favor, digite o nome do nó abaixo.", + "remoteExitNodeConfirmDelete": "Confirmar exclusão do nó", + "remoteExitNodeDelete": "Excluir nó", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Criar nó", + "description": "Crie um novo nó para estender sua conectividade de rede", + "viewAllButton": "Ver Todos os Nós", + "strategy": { + "title": "Estratégia de Criação", + "description": "Escolha isto para configurar o seu nó manualmente ou gerar novas credenciais.", + "adopt": { + "title": "Adotar Nodo", + "description": "Escolha isto se você já tem credenciais para o nó." + }, + "generate": { + "title": "Gerar Chaves", + "description": "Escolha esta opção se você quer gerar novas chaves para o nó" + } + }, + "adopt": { + "title": "Adotar Nodo Existente", + "description": "Digite as credenciais do nó existente que deseja adoptar", + "nodeIdLabel": "Nó ID", + "nodeIdDescription": "O ID do nó existente que você deseja adoptar", + "secretLabel": "Chave Secreta", + "secretDescription": "A chave secreta do nó existente", + "submitButton": "Nó Adotado" + }, + "generate": { + "title": "Credenciais Geradas", + "description": "Use estas credenciais geradas para configurar o seu nó", + "nodeIdTitle": "Nó ID", + "secretTitle": "Chave Secreta", + "saveCredentialsTitle": "Adicionar Credenciais à Configuração", + "saveCredentialsDescription": "Adicione essas credenciais ao arquivo de configuração do seu nodo de Pangolin auto-hospedado para completar a conexão.", + "submitButton": "Criar nó" + }, + "validation": { + "adoptRequired": "ID do nó e Segredo são necessários ao adotar um nó existente" + }, + "errors": { + "loadDefaultsFailed": "Falha ao carregar padrões", + "defaultsNotLoaded": "Padrões não carregados", + "createFailed": "Falha ao criar nó" + }, + "success": { + "created": "Nó criado com sucesso" + } + }, + "remoteExitNodeSelection": "Seleção de nó", + "remoteExitNodeSelectionDescription": "Selecione um nó para encaminhar o tráfego para este site local", + "remoteExitNodeRequired": "Um nó deve ser seleccionado para sites locais", + "noRemoteExitNodesAvailable": "Nenhum nó disponível", + "noRemoteExitNodesAvailableDescription": "Nenhum nó está disponível para esta organização. Crie um nó primeiro para usar sites locais.", + "exitNode": "Nodo de Saída", + "country": "País", + "rulesMatchCountry": "Atualmente baseado no IP de origem", "managedSelfHosted": { "title": "Gerenciado Auto-Hospedado", "description": "Servidor Pangolin auto-hospedado mais confiável e com baixa manutenção com sinos extras e assobiamentos", @@ -1479,7 +1625,7 @@ }, "benefitLessMaintenance": { "title": "Menos manutenção", - "description": "Sem migrações, backups ou infraestrutura extra para gerenciar. Lidamos com isso na nuvem." + "description": "Sem migrações, backups ou infraestrutura extra para gerir. Lidamos com isso na nuvem." }, "benefitCloudFailover": { "title": "Falha na nuvem", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Domínio Internacional Detectado", "willbestoredas": "Será armazenado como:", + "roleMappingDescription": "Determinar como as funções são atribuídas aos usuários quando eles fazem login quando Auto Provisão está habilitada.", + "selectRole": "Selecione uma função", + "roleMappingExpression": "Expressão", + "selectRolePlaceholder": "Escolha uma função", + "selectRoleDescription": "Selecione uma função para atribuir a todos os usuários deste provedor de identidade", + "roleMappingExpressionDescription": "Insira uma expressão JMESPath para extrair informações da função do token de ID", + "idpTenantIdRequired": "ID do inquilino é necessária", + "invalidValue": "Valor Inválido", + "idpTypeLabel": "Tipo de provedor de identidade", + "roleMappingExpressionPlaceholder": "ex.: Contem (grupos, 'administrador') && 'Administrador' 「'Membro'", + "idpGoogleConfiguration": "Configuração do Google", + "idpGoogleConfigurationDescription": "Configurar suas credenciais do Google OAuth2", + "idpGoogleClientIdDescription": "Seu ID de Cliente OAuth2 do Google", + "idpGoogleClientSecretDescription": "Seu Segredo de Cliente OAuth2 do Google", + "idpAzureConfiguration": "Configuração de ID do Azure Entra", + "idpAzureConfigurationDescription": "Configure as suas credenciais do Azure Entra ID OAuth2", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "seu-tenente-id", + "idpAzureTenantIdDescription": "Seu ID do tenant Azure (encontrado na visão geral do diretório ativo Azure)", + "idpAzureClientIdDescription": "Seu ID de Cliente de Registro do App Azure", + "idpAzureClientSecretDescription": "Seu segredo de cliente de registro de aplicativos Azure", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Configuração do Google", + "idpAzureConfigurationTitle": "Configuração de ID do Azure Entra", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Seu ID de Cliente de Registro do App Azure", + "idpAzureClientSecretDescription2": "Seu segredo de cliente de registro de aplicativos Azure", "idpGoogleDescription": "Provedor Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Cabeçalhos Personalizados", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Cabeçalhos devem estar no formato: Nome do Cabeçalho: valor.", + "subnet": "Sub-rede", + "subnetDescription": "A sub-rede para a configuração de rede dessa organização.", + "authPage": "Página de Autenticação", + "authPageDescription": "Configurar a página de autenticação para sua organização", + "authPageDomain": "Domínio de Página Autenticação", + "noDomainSet": "Nenhum domínio definido", + "changeDomain": "Alterar domínio", + "selectDomain": "Selecionar domínio", + "restartCertificate": "Reiniciar Certificado", + "editAuthPageDomain": "Editar Página de Autenticação", + "setAuthPageDomain": "Definir domínio da página de autenticação", + "failedToFetchCertificate": "Falha ao buscar o certificado", + "failedToRestartCertificate": "Falha ao reiniciar o certificado", + "addDomainToEnableCustomAuthPages": "Adicione um domínio para habilitar páginas de autenticação personalizadas para sua organização", + "selectDomainForOrgAuthPage": "Selecione um domínio para a página de autenticação da organização", "domainPickerProvidedDomain": "Domínio fornecido", "domainPickerFreeProvidedDomain": "Domínio fornecido grátis", "domainPickerVerified": "Verificada", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" não pôde ser válido para {domain}.", "domainPickerSubdomainSanitized": "Subdomínio banalizado", "domainPickerSubdomainCorrected": "\"{sub}\" foi corrigido para \"{sanitized}\"", + "orgAuthSignInTitle": "Entrar na sua organização", + "orgAuthChooseIdpDescription": "Escolha o seu provedor de identidade para continuar", + "orgAuthNoIdpConfigured": "Esta organização não tem nenhum provedor de identidade configurado. Você pode entrar com a identidade do seu Pangolin.", + "orgAuthSignInWithPangolin": "Entrar com o Pangolin", + "subscriptionRequiredToUse": "Uma assinatura é necessária para usar esse recurso.", + "idpDisabled": "Provedores de identidade estão desabilitados.", + "orgAuthPageDisabled": "A página de autenticação da organização está desativada.", + "domainRestartedDescription": "Verificação de domínio reiniciado com sucesso", "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", - "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui." } diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 2ec7764a..290d0215 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -168,6 +168,9 @@ "siteSelect": "Выберите сайт", "siteSearch": "Поиск сайта", "siteNotFound": "Сайт не найден.", + "selectCountry": "Выберите страну", + "searchCountries": "Поиск стран...", + "noCountryFound": "Страна не найдена.", "siteSelectionDescription": "Этот сайт предоставит подключение к цели.", "resourceType": "Тип ресурса", "resourceTypeDescription": "Определите, как вы хотите получать доступ к вашему ресурсу", @@ -236,7 +239,7 @@ "accessUserCreate": "Создать пользователя", "accessUserRemove": "Удалить пользователя", "username": "Имя пользователя", - "identityProvider": "Identity Provider", + "identityProvider": "Поставщик удостоверений", "role": "Роль", "nameRequired": "Имя обязательно", "accessRolesManage": "Управление ролями", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Подключено", "idpErrorConnectingTo": "Возникла проблема при подключении к {name}. Пожалуйста, свяжитесь с вашим администратором.", "idpErrorNotFound": "IdP не найден", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Недействительное приглашение", "inviteInvalidDescription": "Ссылка на приглашение недействительна.", "inviteErrorWrongUser": "Приглашение не для этого пользователя", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Поддомен: {subdomain}", "domainPickerNamespace": "Пространство имен: {namespace}", "domainPickerShowMore": "Показать еще", + "regionSelectorTitle": "Выберите регион", + "regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.", + "regionSelectorPlaceholder": "Выбор региона", + "regionSelectorComingSoon": "Скоро будет", + "billingLoadingSubscription": "Загрузка подписки...", + "billingFreeTier": "Бесплатный уровень", + "billingWarningOverLimit": "Предупреждение: Вы превысили одну или несколько границ использования. Ваши сайты не подключатся, пока вы не измените подписку или не скорректируете использование.", + "billingUsageLimitsOverview": "Обзор лимитов использования", + "billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@fossorial.io.", + "billingDataUsage": "Использование данных", + "billingOnlineTime": "Время работы сайта", + "billingUsers": "Активные пользователи", + "billingDomains": "Активные домены", + "billingRemoteExitNodes": "Активные самоуправляемые узлы", + "billingNoLimitConfigured": "Лимит не установлен", + "billingEstimatedPeriod": "Предполагаемый период выставления счетов", + "billingIncludedUsage": "Включенное использование", + "billingIncludedUsageDescription": "Использование, включенное в ваш текущий план подписки", + "billingFreeTierIncludedUsage": "Бесплатное использование ограничений", + "billingIncluded": "включено", + "billingEstimatedTotal": "Предполагаемая сумма:", + "billingNotes": "Заметки", + "billingEstimateNote": "Это приблизительная оценка на основании вашего текущего использования.", + "billingActualChargesMayVary": "Фактические начисления могут отличаться.", + "billingBilledAtEnd": "С вас будет выставлен счет в конце периода выставления счетов.", + "billingModifySubscription": "Изменить подписку", + "billingStartSubscription": "Начать подписку", + "billingRecurringCharge": "Периодический взнос", + "billingManageSubscriptionSettings": "Управляйте настройками и предпочтениями вашей подписки", + "billingNoActiveSubscription": "У вас нет активной подписки. Начните подписку, чтобы увеличить лимиты использования.", + "billingFailedToLoadSubscription": "Не удалось загрузить подписку", + "billingFailedToLoadUsage": "Не удалось загрузить использование", + "billingFailedToGetCheckoutUrl": "Не удалось получить URL-адрес для оплаты", + "billingPleaseTryAgainLater": "Пожалуйста, повторите попытку позже.", + "billingCheckoutError": "Ошибка при оформлении заказа", + "billingFailedToGetPortalUrl": "Не удалось получить URL-адрес портала", + "billingPortalError": "Ошибка портала", + "billingDataUsageInfo": "Вы несете ответственность за все данные, переданные через безопасные туннели при подключении к облаку. Это включает как входящий, так и исходящий трафик на всех ваших сайтах. При достижении лимита ваши сайты будут отключаться до тех пор, пока вы не обновите план или не уменьшите его использование. При использовании узлов не взимается плата.", + "billingOnlineTimeInfo": "Вы тарифицируете на то, как долго ваши сайты будут подключены к облаку. Например, 44 640 минут равны одному сайту, работающему круглосуточно за весь месяц. Когда вы достигните лимита, ваши сайты будут отключаться до тех пор, пока вы не обновите тарифный план или не сократите нагрузку. При использовании узлов не тарифицируется.", + "billingUsersInfo": "С вас взимается плата за каждого пользователя в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей пользователей в вашей организации.", + "billingDomainInfo": "С вас взимается плата за каждый домен в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей доменов в вашей организации.", + "billingRemoteExitNodesInfo": "С вас взимается плата за каждый управляемый узел в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных управляемых узлов в вашей организации.", "domainNotFound": "Домен не найден", "domainNotFoundDescription": "Этот ресурс отключен, так как домен больше не существует в нашей системе. Пожалуйста, установите новый домен для этого ресурса.", "failed": "Ошибка", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Изменения DNS могут занять некоторое время для распространения через интернет. Это может занять от нескольких минут до 48 часов в зависимости от вашего DNS провайдера и настроек TTL.", "resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов", "resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов", + "billingPricingCalculatorLink": "Калькулятор расценок", "signUpTerms": { "IAgreeToThe": "Я согласен с", "termsOfService": "условия использования", @@ -1368,6 +1412,41 @@ "addNewTarget": "Добавить новую цель", "targetsList": "Список целей", "targetErrorDuplicateTargetFound": "Обнаружена дублирующаяся цель", + "healthCheckHealthy": "Здоровый", + "healthCheckUnhealthy": "Нездоровый", + "healthCheckUnknown": "Неизвестно", + "healthCheck": "Проверка здоровья", + "configureHealthCheck": "Настроить проверку здоровья", + "configureHealthCheckDescription": "Настройте мониторинг состояния для {target}", + "enableHealthChecks": "Включить проверки здоровья", + "enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.", + "healthScheme": "Метод", + "healthSelectScheme": "Выберите метод", + "healthCheckPath": "Путь", + "healthHostname": "IP / хост", + "healthPort": "Порт", + "healthCheckPathDescription": "Путь к проверке состояния здоровья.", + "healthyIntervalSeconds": "Интервал здоровых состояний", + "unhealthyIntervalSeconds": "Интервал нездоровых состояний", + "IntervalSeconds": "Интервал здоровых состояний", + "timeoutSeconds": "Тайм-аут", + "timeIsInSeconds": "Время указано в секундах", + "retryAttempts": "Количество попыток повторного запроса", + "expectedResponseCodes": "Ожидаемые коды ответов", + "expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.", + "customHeaders": "Пользовательские заголовки", + "customHeadersDescription": "Заголовки новой строки, разделённые: название заголовка: значение", + "headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.", + "saveHealthCheck": "Сохранить проверку здоровья", + "healthCheckSaved": "Проверка здоровья сохранена", + "healthCheckSavedDescription": "Конфигурация проверки состояния успешно сохранена", + "healthCheckError": "Ошибка проверки состояния", + "healthCheckErrorDescription": "Произошла ошибка при сохранении конфигурации проверки состояния", + "healthCheckPathRequired": "Требуется путь проверки состояния", + "healthCheckMethodRequired": "Требуется метод HTTP", + "healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд", + "healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды", + "healthCheckRetryMin": "Количество попыток должно быть не менее 1", "httpMethod": "HTTP метод", "selectHttpMethod": "Выберите HTTP метод", "domainPickerSubdomainLabel": "Поддомен", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Введите поддомен для поиска и выбора из доступных свободных доменов.", "domainPickerFreeDomains": "Свободные домены", "domainPickerSearchForAvailableDomains": "Поиск доступных доменов", + "domainPickerNotWorkSelfHosted": "Примечание: бесплатные предоставляемые домены в данный момент недоступны для самоуправляемых экземпляров.", "resourceDomain": "Домен", "resourceEditDomain": "Редактировать домен", "siteName": "Имя сайта", @@ -1463,6 +1543,72 @@ "autoLoginError": "Ошибка автоматического входа", "autoLoginErrorNoRedirectUrl": "URL-адрес перенаправления не получен от провайдера удостоверения.", "autoLoginErrorGeneratingUrl": "Не удалось сгенерировать URL-адрес аутентификации.", + "remoteExitNodeManageRemoteExitNodes": "Управление самоуправляемым", + "remoteExitNodeDescription": "Управляйте узлами для расширения сетевого подключения", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Поиск узлов...", + "remoteExitNodeAdd": "Добавить узел", + "remoteExitNodeErrorDelete": "Ошибка удаления узла", + "remoteExitNodeQuestionRemove": "Вы уверены, что хотите удалить узел {selectedNode} из организации?", + "remoteExitNodeMessageRemove": "После удаления узел больше не будет доступен.", + "remoteExitNodeMessageConfirm": "Для подтверждения введите имя узла ниже.", + "remoteExitNodeConfirmDelete": "Подтвердите удаление узла", + "remoteExitNodeDelete": "Удалить узел", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Создать узел", + "description": "Создайте новый узел, чтобы расширить сетевое подключение", + "viewAllButton": "Все узлы", + "strategy": { + "title": "Стратегия создания", + "description": "Выберите эту опцию для настройки вашего узла или создания новых учетных данных.", + "adopt": { + "title": "Принять узел", + "description": "Выберите это, если у вас уже есть учетные данные для узла." + }, + "generate": { + "title": "Сгенерировать ключи", + "description": "Выберите это, если вы хотите создать новые ключи для узла" + } + }, + "adopt": { + "title": "Принять существующий узел", + "description": "Введите учетные данные существующего узла, который вы хотите принять", + "nodeIdLabel": "ID узла", + "nodeIdDescription": "ID существующего узла, который вы хотите принять", + "secretLabel": "Секретный ключ", + "secretDescription": "Секретный ключ существующего узла", + "submitButton": "Принять узел" + }, + "generate": { + "title": "Сгенерированные учетные данные", + "description": "Используйте эти учётные данные для настройки вашего узла", + "nodeIdTitle": "ID узла", + "secretTitle": "Секретный ключ", + "saveCredentialsTitle": "Добавить учетные данные в конфигурацию", + "saveCredentialsDescription": "Добавьте эти учетные данные в файл конфигурации вашего самоуправляемого узла Pangolin, чтобы завершить подключение.", + "submitButton": "Создать узел" + }, + "validation": { + "adoptRequired": "ID узла и секрет требуются при установке существующего узла" + }, + "errors": { + "loadDefaultsFailed": "Не удалось загрузить параметры по умолчанию", + "defaultsNotLoaded": "Параметры по умолчанию не загружены", + "createFailed": "Не удалось создать узел" + }, + "success": { + "created": "Узел успешно создан" + } + }, + "remoteExitNodeSelection": "Выбор узла", + "remoteExitNodeSelectionDescription": "Выберите узел для маршрутизации трафика для этого локального сайта", + "remoteExitNodeRequired": "Узел должен быть выбран для локальных сайтов", + "noRemoteExitNodesAvailable": "Нет доступных узлов", + "noRemoteExitNodesAvailableDescription": "Для этой организации узлы не доступны. Сначала создайте узел, чтобы использовать локальные сайты.", + "exitNode": "Узел выхода", + "country": "Страна", + "rulesMatchCountry": "В настоящее время основано на исходном IP", "managedSelfHosted": { "title": "Управляемый с самовывоза", "description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Обнаружен международный домен", "willbestoredas": "Будет храниться как:", + "roleMappingDescription": "Определите, как роли, назначаемые пользователям, когда они войдут в систему автоматического профиля.", + "selectRole": "Выберите роль", + "roleMappingExpression": "Выражение", + "selectRolePlaceholder": "Выберите роль", + "selectRoleDescription": "Выберите роль, чтобы назначить всем пользователям этого поставщика идентификации", + "roleMappingExpressionDescription": "Введите выражение JMESPath, чтобы извлечь информацию о роли из ID токена", + "idpTenantIdRequired": "Требуется ID владельца", + "invalidValue": "Неверное значение", + "idpTypeLabel": "Тип поставщика удостоверений", + "roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Конфигурация Google", + "idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2", + "idpGoogleClientIdDescription": "Ваш Google OAuth2 ID клиента", + "idpGoogleClientSecretDescription": "Ваш Google OAuth2 Секрет", + "idpAzureConfiguration": "Конфигурация Azure Entra ID", + "idpAzureConfigurationDescription": "Настройте учетные данные Azure Entra ID OAuth2", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "ваш тенант-id", + "idpAzureTenantIdDescription": "Идентификатор арендатора Azure (найден в обзоре Active Directory Azure)", + "idpAzureClientIdDescription": "Ваш идентификатор клиента Azure App", + "idpAzureClientSecretDescription": "Секрет регистрации клиента Azure App", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Конфигурация Google", + "idpAzureConfigurationTitle": "Конфигурация Azure Entra ID", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Ваш идентификатор клиента Azure App", + "idpAzureClientSecretDescription2": "Секрет регистрации клиента Azure App", "idpGoogleDescription": "Google OAuth2/OIDC провайдер", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "Пользовательские заголовки", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.", + "subnet": "Подсеть", + "subnetDescription": "Подсеть для конфигурации сети этой организации.", + "authPage": "Страница авторизации", + "authPageDescription": "Настройка страницы авторизации для вашей организации", + "authPageDomain": "Домен страницы авторизации", + "noDomainSet": "Домен не установлен", + "changeDomain": "Изменить домен", + "selectDomain": "Выберите домен", + "restartCertificate": "Перезапустить сертификат", + "editAuthPageDomain": "Редактировать домен страницы авторизации", + "setAuthPageDomain": "Установить домен страницы авторизации", + "failedToFetchCertificate": "Не удалось получить сертификат", + "failedToRestartCertificate": "Не удалось перезапустить сертификат", + "addDomainToEnableCustomAuthPages": "Добавьте домен для включения пользовательских страниц аутентификации для вашей организации", + "selectDomainForOrgAuthPage": "Выберите домен для страницы аутентификации организации", "domainPickerProvidedDomain": "Домен предоставлен", "domainPickerFreeProvidedDomain": "Бесплатный домен", "domainPickerVerified": "Подтверждено", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не может быть действительным для {domain}.", "domainPickerSubdomainSanitized": "Субдомен очищен", "domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"", + "orgAuthSignInTitle": "Войдите в свою организацию", + "orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения", + "orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.", + "orgAuthSignInWithPangolin": "Войти через Pangolin", + "subscriptionRequiredToUse": "Для использования этой функции требуется подписка.", + "idpDisabled": "Провайдеры идентификации отключены.", + "orgAuthPageDisabled": "Страница авторизации организации отключена.", + "domainRestartedDescription": "Проверка домена успешно перезапущена", "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", - "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда." } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index f0a66950..33c37327 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -168,6 +168,9 @@ "siteSelect": "Site seç", "siteSearch": "Site ara", "siteNotFound": "Herhangi bir site bulunamadı.", + "selectCountry": "Ülke Seç", + "searchCountries": "Ülkeleri ara...", + "noCountryFound": "Ülke bulunamadı.", "siteSelectionDescription": "Bu site hedefe bağlantı sağlayacaktır.", "resourceType": "Kaynak Türü", "resourceTypeDescription": "Kaynağınıza nasıl erişmek istediğinizi belirleyin", @@ -814,7 +817,7 @@ "redirectUrl": "Yönlendirme URL'si", "redirectUrlAbout": "Yönlendirme URL'si Hakkında", "redirectUrlAboutDescription": "Bu, kimlik doğrulamasından sonra kullanıcıların yönlendirileceği URL'dir. Bu URL'yi kimlik sağlayıcınızın ayarlarında yapılandırmanız gerekir.", - "pangolinAuth": "Auth - Pangolin", + "pangolinAuth": "Yetkilendirme - Pangolin", "verificationCodeLengthRequirements": "Doğrulama kodunuz 8 karakter olmalıdır.", "errorOccurred": "Bir hata oluştu", "emailErrorVerify": "E-posta doğrulanamadı: ", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Bağlandı", "idpErrorConnectingTo": "{name} ile bağlantı kurarken bir sorun meydana geldi. Lütfen yöneticiye danışın.", "idpErrorNotFound": "IdP bulunamadı", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Geçersiz Davet", "inviteInvalidDescription": "Davet bağlantısı geçersiz.", "inviteErrorWrongUser": "Davet bu kullanıcı için değil", @@ -1241,7 +1242,7 @@ "sidebarExpand": "Genişlet", "newtUpdateAvailable": "Güncelleme Mevcut", "newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.", - "domainPickerEnterDomain": "Domain", + "domainPickerEnterDomain": "Alan Adı", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.", "domainPickerDescriptionSaas": "Mevcut seçenekleri görmek için tam etki alanı, alt etki alanı veya sadece bir isim girin", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Alt Alan: {subdomain}", "domainPickerNamespace": "Ad Alanı: {namespace}", "domainPickerShowMore": "Daha Fazla Göster", + "regionSelectorTitle": "Bölge Seç", + "regionSelectorInfo": "Bir bölge seçmek, konumunuz için daha iyi performans sağlamamıza yardımcı olur. Sunucunuzla aynı bölgede olmanıza gerek yoktur.", + "regionSelectorPlaceholder": "Bölge Seçin", + "regionSelectorComingSoon": "Yakında Geliyor", + "billingLoadingSubscription": "Abonelik yükleniyor...", + "billingFreeTier": "Ücretsiz Dilim", + "billingWarningOverLimit": "Uyarı: Bir veya daha fazla kullanım limitini aştınız. Aboneliğinizi değiştirmediğiniz veya kullanımı ayarlamadığınız sürece siteleriniz bağlanmayacaktır.", + "billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü", + "billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@fossorial.io adresinden bizimle iletişime geçin.", + "billingDataUsage": "Veri Kullanımı", + "billingOnlineTime": "Site Çevrimiçi Süresi", + "billingUsers": "Aktif Kullanıcılar", + "billingDomains": "Aktif Alanlar", + "billingRemoteExitNodes": "Aktif Öz-Host Düğümleri", + "billingNoLimitConfigured": "Hiçbir limit yapılandırılmadı", + "billingEstimatedPeriod": "Tahmini Fatura Dönemi", + "billingIncludedUsage": "Dahil Kullanım", + "billingIncludedUsageDescription": "Mevcut abonelik planınıza bağlı kullanım", + "billingFreeTierIncludedUsage": "Ücretsiz dilim kullanım hakları", + "billingIncluded": "dahil", + "billingEstimatedTotal": "Tahmini Toplam:", + "billingNotes": "Notlar", + "billingEstimateNote": "Bu, mevcut kullanımınıza dayalı bir tahmindir.", + "billingActualChargesMayVary": "Asıl ücretler farklılık gösterebilir.", + "billingBilledAtEnd": "Fatura döneminin sonunda fatura düzenlenecektir.", + "billingModifySubscription": "Aboneliği Düzenle", + "billingStartSubscription": "Aboneliği Başlat", + "billingRecurringCharge": "Yinelenen Ücret", + "billingManageSubscriptionSettings": "Abonelik ayarlarınızı ve tercihlerinizi yönetin", + "billingNoActiveSubscription": "Aktif bir aboneliğiniz yok. Kullanım limitlerini artırmak için aboneliğinizi başlatın.", + "billingFailedToLoadSubscription": "Abonelik yüklenemedi", + "billingFailedToLoadUsage": "Kullanım yüklenemedi", + "billingFailedToGetCheckoutUrl": "Ödeme URL'si alınamadı", + "billingPleaseTryAgainLater": "Lütfen daha sonra tekrar deneyin.", + "billingCheckoutError": "Ödeme Hatası", + "billingFailedToGetPortalUrl": "Portal URL'si alınamadı", + "billingPortalError": "Portal Hatası", + "billingDataUsageInfo": "Buluta bağlandığınızda, güvenli tünellerinizden aktarılan tüm verilerden ücret alınırsınız. Bu, tüm sitelerinizdeki gelen ve giden trafiği içerir. Limitinize ulaştığınızda, planınızı yükseltmeli veya kullanımı azaltmalısınız, aksi takdirde siteleriniz bağlantıyı keser. Düğümler kullanırken verilerden ücret alınmaz.", + "billingOnlineTimeInfo": "Sitelerinizin buluta ne kadar süre bağlı kaldığına göre ücretlendirilirsiniz. Örneğin, 44,640 dakika, bir sitenin 24/7 boyunca tam bir ay boyunca çalışması anlamına gelir. Limitinize ulaştığınızda, planınızı yükseltmeyip kullanımı azaltmazsanız siteleriniz bağlantıyı keser. Düğümler kullanırken zamandan ücret alınmaz.", + "billingUsersInfo": "Kuruluşunuzdaki her kullanıcı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif kullanıcı hesaplarının sayısına göre günlük olarak hesaplanır.", + "billingDomainInfo": "Kuruluşunuzdaki her alan adı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif alan adları hesaplarının sayısına göre günlük olarak hesaplanır.", + "billingRemoteExitNodesInfo": "Kuruluşunuzdaki her yönetilen Düğüm için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif yönetilen Düğümler sayısına göre günlük olarak hesaplanır.", "domainNotFound": "Alan Adı Bulunamadı", "domainNotFoundDescription": "Bu kaynak devre dışıdır çünkü alan adı sistemimizde artık mevcut değil. Bu kaynak için yeni bir alan adı belirleyin.", "failed": "Başarısız", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS değişikliklerinin internet genelinde yayılması zaman alabilir. DNS sağlayıcınız ve TTL ayarlarına bağlı olarak bu birkaç dakika ile 48 saat arasında değişebilir.", "resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir", "resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı", + "billingPricingCalculatorLink": "Fiyat Hesaplayıcı", "signUpTerms": { "IAgreeToThe": "Kabul ediyorum", "termsOfService": "hizmet şartları", @@ -1368,6 +1412,41 @@ "addNewTarget": "Yeni Hedef Ekle", "targetsList": "Hedefler Listesi", "targetErrorDuplicateTargetFound": "Yinelenen hedef bulundu", + "healthCheckHealthy": "Sağlıklı", + "healthCheckUnhealthy": "Sağlıksız", + "healthCheckUnknown": "Bilinmiyor", + "healthCheck": "Sağlık Kontrolü", + "configureHealthCheck": "Sağlık Kontrolünü Yapılandır", + "configureHealthCheckDescription": "{hedef} için sağlık izleme kurun", + "enableHealthChecks": "Sağlık Kontrollerini Etkinleştir", + "enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.", + "healthScheme": "Yöntem", + "healthSelectScheme": "Yöntem Seç", + "healthCheckPath": "Yol", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "Sağlık durumunu kontrol etmek için yol.", + "healthyIntervalSeconds": "Sağlıklı Aralık", + "unhealthyIntervalSeconds": "Sağlıksız Aralık", + "IntervalSeconds": "Sağlıklı Aralık", + "timeoutSeconds": "Zaman Aşımı", + "timeIsInSeconds": "Zaman saniye cinsindendir", + "retryAttempts": "Tekrar Deneme Girişimleri", + "expectedResponseCodes": "Beklenen Yanıt Kodları", + "expectedResponseCodesDescription": "Sağlıklı durumu gösteren HTTP durum kodu. Boş bırakılırsa, 200-300 arası sağlıklı kabul edilir.", + "customHeaders": "Özel Başlıklar", + "customHeadersDescription": "Başlıklar yeni satırla ayrılmış: Başlık-Adı: değer", + "headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.", + "saveHealthCheck": "Sağlık Kontrolünü Kaydet", + "healthCheckSaved": "Sağlık Kontrolü Kaydedildi", + "healthCheckSavedDescription": "Sağlık kontrol yapılandırması başarıyla kaydedildi", + "healthCheckError": "Sağlık Kontrol Hatası", + "healthCheckErrorDescription": "Sağlık kontrol yapılandırması kaydedilirken bir hata oluştu", + "healthCheckPathRequired": "Sağlık kontrol yolu gereklidir", + "healthCheckMethodRequired": "HTTP yöntemi gereklidir", + "healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır", + "healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır", + "healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır", "httpMethod": "HTTP Yöntemi", "selectHttpMethod": "HTTP yöntemini seçin", "domainPickerSubdomainLabel": "Alt Alan Adı", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Mevcut ücretsiz alan adları arasından aramak ve seçmek için bir alt alan adı girin.", "domainPickerFreeDomains": "Ücretsiz Alan Adları", "domainPickerSearchForAvailableDomains": "Mevcut alan adlarını ara", + "domainPickerNotWorkSelfHosted": "Not: Ücretsiz sağlanan alan adları şu anda öz-host edilmiş örnekler için kullanılabilir değildir.", "resourceDomain": "Alan Adı", "resourceEditDomain": "Alan Adını Düzenle", "siteName": "Site Adı", @@ -1463,6 +1543,72 @@ "autoLoginError": "Otomatik Giriş Hatası", "autoLoginErrorNoRedirectUrl": "Kimlik sağlayıcıdan yönlendirme URL'si alınamadı.", "autoLoginErrorGeneratingUrl": "Kimlik doğrulama URL'si oluşturulamadı.", + "remoteExitNodeManageRemoteExitNodes": "Öz-Host Yönetim", + "remoteExitNodeDescription": "Ağ bağlantınızı genişletmek için düğümleri yönetin", + "remoteExitNodes": "Düğümler", + "searchRemoteExitNodes": "Düğüm ara...", + "remoteExitNodeAdd": "Düğüm Ekle", + "remoteExitNodeErrorDelete": "Düğüm silinirken hata oluştu", + "remoteExitNodeQuestionRemove": "{selectedNode} düğümünü organizasyondan kaldırmak istediğinizden emin misiniz?", + "remoteExitNodeMessageRemove": "Kaldırıldığında, düğüm artık erişilebilir olmayacaktır.", + "remoteExitNodeMessageConfirm": "Onaylamak için lütfen aşağıya düğümün adını yazın.", + "remoteExitNodeConfirmDelete": "Düğüm Silmeyi Onayla", + "remoteExitNodeDelete": "Düğümü Sil", + "sidebarRemoteExitNodes": "Düğümler", + "remoteExitNodeCreate": { + "title": "Düğüm Oluştur", + "description": "Ağ bağlantınızı genişletmek için yeni bir düğüm oluşturun", + "viewAllButton": "Tüm Düğümleri Gör", + "strategy": { + "title": "Oluşturma Stratejisi", + "description": "Düğümünüzü manuel olarak yapılandırmak veya yeni kimlik bilgileri oluşturmak için bunu seçin.", + "adopt": { + "title": "Düğüm Benimse", + "description": "Zaten düğüm için kimlik bilgilerine sahipseniz bunu seçin." + }, + "generate": { + "title": "Anahtarları Oluştur", + "description": "Düğüm için yeni anahtarlar oluşturmak istiyorsanız bunu seçin" + } + }, + "adopt": { + "title": "Mevcut Düğümü Benimse", + "description": "Adayacağınız mevcut düğümün kimlik bilgilerini girin", + "nodeIdLabel": "Düğüm ID", + "nodeIdDescription": "Adayacağınız mevcut düğümün ID'si", + "secretLabel": "Gizli", + "secretDescription": "Mevcut düğümün gizli anahtarı", + "submitButton": "Düğümü Benimse" + }, + "generate": { + "title": "Oluşturulan Kimlik Bilgileri", + "description": "Düğümünüzü yapılandırmak için oluşturulan bu kimlik bilgilerini kullanın", + "nodeIdTitle": "Düğüm ID", + "secretTitle": "Gizli", + "saveCredentialsTitle": "Kimlik Bilgilerini Yapılandırmaya Ekle", + "saveCredentialsDescription": "Bağlantıyı tamamlamak için bu kimlik bilgilerini öz-host Pangolin düğüm yapılandırma dosyanıza ekleyin.", + "submitButton": "Düğüm Oluştur" + }, + "validation": { + "adoptRequired": "Mevcut bir düğümü benimserken Düğüm ID ve Gizli anahtar gereklidir" + }, + "errors": { + "loadDefaultsFailed": "Varsayılanlar yüklenemedi", + "defaultsNotLoaded": "Varsayılanlar yüklenmedi", + "createFailed": "Düğüm oluşturulamadı" + }, + "success": { + "created": "Düğüm başarıyla oluşturuldu" + } + }, + "remoteExitNodeSelection": "Düğüm Seçimi", + "remoteExitNodeSelectionDescription": "Yerel site için trafiği yönlendirecek düğümü seçin", + "remoteExitNodeRequired": "Yerel siteler için bir düğüm seçilmelidir", + "noRemoteExitNodesAvailable": "Düğüm Bulunamadı", + "noRemoteExitNodesAvailableDescription": "Bu organizasyon için düğüm mevcut değil. Yerel siteleri kullanmak için önce bir düğüm oluşturun.", + "exitNode": "Çıkış Düğümü", + "country": "Ülke", + "rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak", "managedSelfHosted": { "title": "Yönetilen Self-Hosted", "description": "Daha güvenilir ve düşük bakım gerektiren, ekstra özelliklere sahip kendi kendine barındırabileceğiniz Pangolin sunucusu", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Uluslararası Alan Adı Tespit Edildi", "willbestoredas": "Şu şekilde depolanacak:", + "roleMappingDescription": "Otomatik Sağlama etkinleştirildiğinde kullanıcıların oturum açarken rollerin nasıl atandığını belirleyin.", + "selectRole": "Bir Rol Seçin", + "roleMappingExpression": "İfade", + "selectRolePlaceholder": "Bir rol seçin", + "selectRoleDescription": "Bu kimlik sağlayıcısından tüm kullanıcılara atanacak bir rol seçin", + "roleMappingExpressionDescription": "Rol bilgilerini ID tokeninden çıkarmak için bir JMESPath ifadesi girin", + "idpTenantIdRequired": "Kiracı Kimliği gereklidir", + "invalidValue": "Geçersiz değer", + "idpTypeLabel": "Kimlik Sağlayıcı Türü", + "roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'", + "idpGoogleConfiguration": "Google Yapılandırması", + "idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın", + "idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz", + "idpGoogleClientSecretDescription": "Google OAuth2 İstemci Sırrınız", + "idpAzureConfiguration": "Azure Entra ID Yapılandırması", + "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 kimlik bilgilerinizi yapılandırın", + "idpTenantId": "Kiracı Kimliği", + "idpTenantIdPlaceholder": "kiraci-kimliginiz", + "idpAzureTenantIdDescription": "Azure kiracı kimliğiniz (Azure Active Directory genel bakışında bulunur)", + "idpAzureClientIdDescription": "Azure Uygulama Kaydı İstemci Kimliğiniz", + "idpAzureClientSecretDescription": "Azure Uygulama Kaydı İstemci Sırrınız", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Yapılandırması", + "idpAzureConfigurationTitle": "Azure Entra ID Yapılandırması", + "idpTenantIdLabel": "Kiracı Kimliği", + "idpAzureClientIdDescription2": "Azure Uygulama Kaydı İstemci Kimliğiniz", + "idpAzureClientSecretDescription2": "Azure Uygulama Kaydı İstemci Sırrınız", "idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı", - "customHeaders": "Özel Başlıklar", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.", + "subnet": "Alt ağ", + "subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.", + "authPage": "Yetkilendirme Sayfası", + "authPageDescription": "Kuruluşunuz için yetkilendirme sayfasını yapılandırın", + "authPageDomain": "Yetkilendirme Sayfası Alanı", + "noDomainSet": "Alan belirlenmedi", + "changeDomain": "Alanı Değiştir", + "selectDomain": "Alan Seçin", + "restartCertificate": "Sertifikayı Yenile", + "editAuthPageDomain": "Yetkilendirme Sayfası Alanını Düzenle", + "setAuthPageDomain": "Yetkilendirme Sayfası Alanını Ayarla", + "failedToFetchCertificate": "Sertifika getirilemedi", + "failedToRestartCertificate": "Sertifika yeniden başlatılamadı", + "addDomainToEnableCustomAuthPages": "Kuruluşunuz için özel kimlik doğrulama sayfalarını etkinleştirmek için bir alan ekleyin", + "selectDomainForOrgAuthPage": "Kuruluşun kimlik doğrulama sayfası için bir alan seçin", "domainPickerProvidedDomain": "Sağlanan Alan Adı", "domainPickerFreeProvidedDomain": "Ücretsiz Sağlanan Alan Adı", "domainPickerVerified": "Doğrulandı", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" {domain} için geçerli yapılamadı.", "domainPickerSubdomainSanitized": "Alt alan adı temizlendi", "domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi", + "orgAuthSignInTitle": "Kuruluşunuza giriş yapın", + "orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin", + "orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.", + "orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap", + "subscriptionRequiredToUse": "Bu özelliği kullanmak için abonelik gerekmektedir.", + "idpDisabled": "Kimlik sağlayıcılar devre dışı bırakılmıştır.", + "orgAuthPageDisabled": "Kuruluş kimlik doğrulama sayfası devre dışı bırakılmıştır.", + "domainRestartedDescription": "Alan doğrulaması başarıyla yeniden başlatıldı", "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", - "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün." } diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 1e6ddce8..5708f976 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -168,6 +168,9 @@ "siteSelect": "选择站点", "siteSearch": "搜索站点", "siteNotFound": "未找到站点。", + "selectCountry": "选择国家", + "searchCountries": "搜索国家...", + "noCountryFound": "找不到国家。", "siteSelectionDescription": "此站点将为目标提供连接。", "resourceType": "资源类型", "resourceTypeDescription": "确定如何访问您的资源", @@ -595,7 +598,7 @@ "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", "newtEndpoint": "Newt 端点", - "newtId": "Newt ID", + "newtId": "Newt ID", "newtSecretKey": "Newt 私钥", "architecture": "架构", "sites": "站点", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "已连接", "idpErrorConnectingTo": "无法连接到 {name},请联系管理员协助处理。", "idpErrorNotFound": "找不到 IdP", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "无效邀请", "inviteInvalidDescription": "邀请链接无效。", "inviteErrorWrongUser": "邀请不是该用户的", @@ -1155,7 +1156,7 @@ "containerLabels": "标签", "containerLabelsCount": "{count, plural, other {# 标签}}", "containerLabelsTitle": "容器标签", - "containerLabelEmpty": "", + "containerLabelEmpty": "<为空>", "containerPorts": "端口", "containerPortsMore": "+{count} 更多", "containerActions": "行动", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "子域:{subdomain}", "domainPickerNamespace": "命名空间:{namespace}", "domainPickerShowMore": "显示更多", + "regionSelectorTitle": "选择区域", + "regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。", + "regionSelectorPlaceholder": "选择一个区域", + "regionSelectorComingSoon": "即将推出", + "billingLoadingSubscription": "正在加载订阅...", + "billingFreeTier": "免费层", + "billingWarningOverLimit": "警告:您已超出一个或多个使用限制。在您修改订阅或调整使用情况之前,您的站点将无法连接。", + "billingUsageLimitsOverview": "使用限制概览", + "billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@fossorial.io。", + "billingDataUsage": "数据使用情况", + "billingOnlineTime": "站点在线时间", + "billingUsers": "活跃用户", + "billingDomains": "活跃域", + "billingRemoteExitNodes": "活跃自托管节点", + "billingNoLimitConfigured": "未配置限制", + "billingEstimatedPeriod": "估计结算周期", + "billingIncludedUsage": "包含的使用量", + "billingIncludedUsageDescription": "您当前订阅计划中包含的使用量", + "billingFreeTierIncludedUsage": "免费层使用额度", + "billingIncluded": "包含", + "billingEstimatedTotal": "预计总额:", + "billingNotes": "备注", + "billingEstimateNote": "这是根据您当前使用情况的估算。", + "billingActualChargesMayVary": "实际费用可能会有变化。", + "billingBilledAtEnd": "您将在结算周期结束时被计费。", + "billingModifySubscription": "修改订阅", + "billingStartSubscription": "开始订阅", + "billingRecurringCharge": "周期性收费", + "billingManageSubscriptionSettings": "管理您的订阅设置和偏好", + "billingNoActiveSubscription": "您没有活跃的订阅。开始订阅以增加使用限制。", + "billingFailedToLoadSubscription": "无法加载订阅", + "billingFailedToLoadUsage": "无法加载使用情况", + "billingFailedToGetCheckoutUrl": "无法获取结账网址", + "billingPleaseTryAgainLater": "请稍后再试。", + "billingCheckoutError": "结账错误", + "billingFailedToGetPortalUrl": "无法获取门户网址", + "billingPortalError": "门户错误", + "billingDataUsageInfo": "当连接到云端时,您将为通过安全隧道传输的所有数据收取费用。 这包括您所有站点的进出流量。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取数据。", + "billingOnlineTimeInfo": "您要根据您的网站连接到云端的时间长短收取费用。 例如,44,640分钟等于一个24/7全月运行的网站。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取费用。", + "billingUsersInfo": "根据您组织中的活跃用户数量收费。按日计算账单。", + "billingDomainInfo": "根据组织中活跃域的数量收费。按日计算账单。", + "billingRemoteExitNodesInfo": "根据您组织中已管理节点的数量收费。按日计算账单。", "domainNotFound": "域未找到", "domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。", "failed": "失败", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 更改可能需要一些时间才能在互联网上传播。这可能需要从几分钟到 48 小时,具体取决于您的 DNS 提供商和 TTL 设置。", "resourcePortRequired": "非 HTTP 资源必须输入端口号", "resourcePortNotAllowed": "HTTP 资源不应设置端口号", + "billingPricingCalculatorLink": "价格计算器", "signUpTerms": { "IAgreeToThe": "我同意", "termsOfService": "服务条款", @@ -1346,7 +1390,7 @@ "clientOlmCredentials": "Olm 凭据", "clientOlmCredentialsDescription": "这是 Olm 服务器的身份验证方式", "olmEndpoint": "Olm 端点", - "olmId": "Olm ID", + "olmId": "Olm ID", "olmSecretKey": "Olm 私钥", "clientCredentialsSave": "保存您的凭据", "clientCredentialsSaveDescription": "该信息仅会显示一次,请确保将其复制到安全位置。", @@ -1368,6 +1412,41 @@ "addNewTarget": "添加新目标", "targetsList": "目标列表", "targetErrorDuplicateTargetFound": "找到重复的目标", + "healthCheckHealthy": "正常", + "healthCheckUnhealthy": "不正常", + "healthCheckUnknown": "未知", + "healthCheck": "健康检查", + "configureHealthCheck": "配置健康检查", + "configureHealthCheckDescription": "为 {target} 设置健康监控", + "enableHealthChecks": "启用健康检查", + "enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。", + "healthScheme": "方法", + "healthSelectScheme": "选择方法", + "healthCheckPath": "路径", + "healthHostname": "IP / 主机", + "healthPort": "端口", + "healthCheckPathDescription": "用于检查健康状态的路径。", + "healthyIntervalSeconds": "正常间隔", + "unhealthyIntervalSeconds": "不正常间隔", + "IntervalSeconds": "正常间隔", + "timeoutSeconds": "超时", + "timeIsInSeconds": "时间以秒为单位", + "retryAttempts": "重试次数", + "expectedResponseCodes": "期望响应代码", + "expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空,200-300 被视为健康。", + "customHeaders": "自定义标题", + "customHeadersDescription": "头部新行分隔:头部名称:值", + "headersValidationError": "头部必须是格式:头部名称:值。", + "saveHealthCheck": "保存健康检查", + "healthCheckSaved": "健康检查已保存", + "healthCheckSavedDescription": "健康检查配置已成功保存。", + "healthCheckError": "健康检查错误", + "healthCheckErrorDescription": "保存健康检查配置时出错", + "healthCheckPathRequired": "健康检查路径为必填项", + "healthCheckMethodRequired": "HTTP 方法为必填项", + "healthCheckIntervalMin": "检查间隔必须至少为 5 秒", + "healthCheckTimeoutMin": "超时必须至少为 1 秒", + "healthCheckRetryMin": "重试次数必须至少为 1 次", "httpMethod": "HTTP 方法", "selectHttpMethod": "选择 HTTP 方法", "domainPickerSubdomainLabel": "子域名", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "输入一个子域名以搜索并从可用免费域名中选择。", "domainPickerFreeDomains": "免费域名", "domainPickerSearchForAvailableDomains": "搜索可用域名", + "domainPickerNotWorkSelfHosted": "注意:自托管实例当前不提供免费的域名。", "resourceDomain": "域名", "resourceEditDomain": "编辑域名", "siteName": "站点名称", @@ -1463,6 +1543,72 @@ "autoLoginError": "自动登录错误", "autoLoginErrorNoRedirectUrl": "未从身份提供商收到重定向URL。", "autoLoginErrorGeneratingUrl": "生成身份验证URL失败。", + "remoteExitNodeManageRemoteExitNodes": "管理自托管", + "remoteExitNodeDescription": "管理节点以扩展您的网络连接", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "搜索节点...", + "remoteExitNodeAdd": "添加节点", + "remoteExitNodeErrorDelete": "删除节点时出错", + "remoteExitNodeQuestionRemove": "您确定要从组织中删除 {selectedNode} 节点吗?", + "remoteExitNodeMessageRemove": "一旦删除,该节点将不再能够访问。", + "remoteExitNodeMessageConfirm": "要确认,请输入以下节点的名称。", + "remoteExitNodeConfirmDelete": "确认删除节点", + "remoteExitNodeDelete": "删除节点", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "创建节点", + "description": "创建一个新节点来扩展您的网络连接", + "viewAllButton": "查看所有节点", + "strategy": { + "title": "创建策略", + "description": "选择此选项以手动配置您的节点或生成新凭据。", + "adopt": { + "title": "采纳节点", + "description": "如果您已经拥有该节点的凭据,请选择此项。" + }, + "generate": { + "title": "生成密钥", + "description": "如果您想为节点生成新密钥,请选择此选项" + } + }, + "adopt": { + "title": "采纳现有节点", + "description": "输入您想要采用的现有节点的凭据", + "nodeIdLabel": "节点 ID", + "nodeIdDescription": "您想要采用的现有节点的 ID", + "secretLabel": "密钥", + "secretDescription": "现有节点的秘密密钥", + "submitButton": "采用节点" + }, + "generate": { + "title": "生成的凭据", + "description": "使用这些生成的凭据来配置您的节点", + "nodeIdTitle": "节点 ID", + "secretTitle": "密钥", + "saveCredentialsTitle": "将凭据添加到配置中", + "saveCredentialsDescription": "将这些凭据添加到您的自托管 Pangolin 节点配置文件中以完成连接。", + "submitButton": "创建节点" + }, + "validation": { + "adoptRequired": "在通过现有节点时需要节点ID和密钥" + }, + "errors": { + "loadDefaultsFailed": "无法加载默认值", + "defaultsNotLoaded": "默认值未加载", + "createFailed": "创建节点失败" + }, + "success": { + "created": "节点创建成功" + } + }, + "remoteExitNodeSelection": "节点选择", + "remoteExitNodeSelectionDescription": "为此本地站点选择要路由流量的节点", + "remoteExitNodeRequired": "必须为本地站点选择节点", + "noRemoteExitNodesAvailable": "无可用节点", + "noRemoteExitNodesAvailableDescription": "此组织没有可用的节点。首先创建一个节点来使用本地站点。", + "exitNode": "出口节点", + "country": "国家", + "rulesMatchCountry": "当前基于源 IP", "managedSelfHosted": { "title": "托管自托管", "description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "检测到国际域", "willbestoredas": "储存为:", + "roleMappingDescription": "确定当用户启用自动配送时如何分配他们的角色。", + "selectRole": "选择角色", + "roleMappingExpression": "表达式", + "selectRolePlaceholder": "选择角色", + "selectRoleDescription": "选择一个角色,从此身份提供商分配给所有用户", + "roleMappingExpressionDescription": "输入一个 JMESPath 表达式来从 ID 令牌提取角色信息", + "idpTenantIdRequired": "租户ID是必需的", + "invalidValue": "无效的值", + "idpTypeLabel": "身份提供者类型", + "roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'", + "idpGoogleConfiguration": "Google 配置", + "idpGoogleConfigurationDescription": "配置您的 Google OAuth2 凭据", + "idpGoogleClientIdDescription": "您的 Google OAuth2 客户端 ID", + "idpGoogleClientSecretDescription": "您的 Google OAuth2 客户端密钥", + "idpAzureConfiguration": "Azure Entra ID 配置", + "idpAzureConfigurationDescription": "配置您的 Azure Entra ID OAuth2 凭据", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "您的租户ID", + "idpAzureTenantIdDescription": "您的 Azure 租户ID (在 Azure Active Directory 概览中发现)", + "idpAzureClientIdDescription": "您的 Azure 应用程序注册客户端 ID", + "idpAzureClientSecretDescription": "您的 Azure 应用程序注册客户端密钥", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google 配置", + "idpAzureConfigurationTitle": "Azure Entra ID 配置", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "您的 Azure 应用程序注册客户端 ID", + "idpAzureClientSecretDescription2": "您的 Azure 应用程序注册客户端密钥", "idpGoogleDescription": "Google OAuth2/OIDC 提供商", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "customHeaders": "自定义标题", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "头部必须是格式:头部名称:值。", + "subnet": "子网", + "subnetDescription": "此组织网络配置的子网。", + "authPage": "认证页面", + "authPageDescription": "配置您的组织认证页面", + "authPageDomain": "认证页面域", + "noDomainSet": "没有域设置", + "changeDomain": "更改域", + "selectDomain": "选择域", + "restartCertificate": "重新启动证书", + "editAuthPageDomain": "编辑认证页面域", + "setAuthPageDomain": "设置认证页面域", + "failedToFetchCertificate": "获取证书失败", + "failedToRestartCertificate": "重新启动证书失败", + "addDomainToEnableCustomAuthPages": "为您的组织添加域名以启用自定义认证页面", + "selectDomainForOrgAuthPage": "选择组织认证页面的域", "domainPickerProvidedDomain": "提供的域", "domainPickerFreeProvidedDomain": "免费提供的域", "domainPickerVerified": "已验证", @@ -1519,10 +1707,16 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" 无法为 {domain} 变为有效。", "domainPickerSubdomainSanitized": "子域已净化", "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"", + "orgAuthSignInTitle": "登录到您的组织", + "orgAuthChooseIdpDescription": "选择您的身份提供商以继续", + "orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。", + "orgAuthSignInWithPangolin": "使用 Pangolin 登录", + "subscriptionRequiredToUse": "需要订阅才能使用此功能。", + "idpDisabled": "身份提供者已禁用。", + "orgAuthPageDisabled": "组织认证页面已禁用。", + "domainRestartedDescription": "域验证重新启动成功", "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", - "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。" } diff --git a/package-lock.json b/package-lock.json index 0985af1e..14d08688 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", + "@aws-sdk/client-s3": "3.837.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -61,10 +62,12 @@ "http-errors": "2.0.0", "i": "^0.3.7", "input-otp": "1.4.2", + "ioredis": "5.6.1", "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.544.0", + "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.3", "next-intl": "^4.3.9", @@ -83,7 +86,10 @@ "react-hook-form": "7.62.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", + "reodotdev": "^1.0.0", + "resend": "^6.1.1", "semver": "^7.7.2", + "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", "tailwind-merge": "3.3.1", "tw-animate-css": "^1.3.8", @@ -99,6 +105,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", + "@react-email/preview-server": "4.1.0", "@tailwindcss/postcss": "^4.1.13", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", @@ -143,6 +150,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@asteasolutions/zod-to-openapi": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz", @@ -155,11 +176,87 @@ "zod": "^3.20.2" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -175,7 +272,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -188,7 +284,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -202,7 +297,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -216,7 +310,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -231,7 +324,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -241,7 +333,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", @@ -253,7 +344,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -266,7 +356,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -280,7 +369,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -290,51 +378,133 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.837.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.837.0.tgz", + "integrity": "sha512-sBjPPG30HIfNwpzWuajCDf7agb4YAxPFFpsp3kwgptJF8PEi0HzQg64bskquMzjqLC2tXsn5rKtDVpQOvs29MQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-node": "3.835.0", + "@aws-sdk/middleware-bucket-endpoint": "3.830.0", + "@aws-sdk/middleware-expect-continue": "3.821.0", + "@aws-sdk/middleware-flexible-checksums": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-location-constraint": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-s3": "3.835.0", + "@aws-sdk/middleware-ssec": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/signature-v4-multi-region": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.5", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-sesv2": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.888.0.tgz", - "integrity": "sha512-Zy7AXvj4oVLE5Zkj61qYZxIFgJXbRgTmFJvQ/EqgxE87KPR9+gF5wtC3iqcKEmkqFlWlxWrlhV4K70Vqqj4bZQ==", + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.899.0.tgz", + "integrity": "sha512-aMs3QgB9lWaKKrnx9KhIopoeXLNzI/sqdp5M56j30jlBD4vqdcCzW2OwFAAs26QzUgNKOOSY+iLZcE9DUDdIvg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.888.0", - "@aws-sdk/credential-provider-node": "3.888.0", - "@aws-sdk/middleware-host-header": "3.887.0", - "@aws-sdk/middleware-logger": "3.887.0", - "@aws-sdk/middleware-recursion-detection": "3.887.0", - "@aws-sdk/middleware-user-agent": "3.888.0", - "@aws-sdk/region-config-resolver": "3.887.0", - "@aws-sdk/signature-v4-multi-region": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@aws-sdk/util-endpoints": "3.887.0", - "@aws-sdk/util-user-agent-browser": "3.887.0", - "@aws-sdk/util-user-agent-node": "3.888.0", - "@smithy/config-resolver": "^4.2.1", - "@smithy/core": "^3.11.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/credential-provider-node": "3.899.0", + "@aws-sdk/middleware-host-header": "3.893.0", + "@aws-sdk/middleware-logger": "3.893.0", + "@aws-sdk/middleware-recursion-detection": "3.893.0", + "@aws-sdk/middleware-user-agent": "3.899.0", + "@aws-sdk/region-config-resolver": "3.893.0", + "@aws-sdk/signature-v4-multi-region": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/util-endpoints": "3.895.0", + "@aws-sdk/util-user-agent-browser": "3.893.0", + "@aws-sdk/util-user-agent-node": "3.899.0", + "@smithy/config-resolver": "^4.2.2", + "@smithy/core": "^3.13.0", "@smithy/fetch-http-handler": "^5.2.1", "@smithy/hash-node": "^4.1.1", "@smithy/invalid-dependency": "^4.1.1", "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.1", - "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-endpoint": "^4.2.5", + "@smithy/middleware-retry": "^4.3.1", "@smithy/middleware-serde": "^4.1.1", "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/node-http-handler": "^4.2.1", "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", "@smithy/util-base64": "^4.1.0", "@smithy/util-body-length-browser": "^4.1.0", "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.1", - "@smithy/util-defaults-mode-node": "^4.1.1", - "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-defaults-mode-browser": "^4.1.5", + "@smithy/util-defaults-mode-node": "^4.1.5", + "@smithy/util-endpoints": "^3.1.2", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.1", + "@smithy/util-retry": "^4.1.2", "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, @@ -342,49 +512,49 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.888.0.tgz", - "integrity": "sha512-8CLy/ehGKUmekjH+VtZJ4w40PqDg3u0K7uPziq/4P8Q7LLgsy8YQoHNbuY4am7JU3HWrqLXJI9aaz1+vPGPoWA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/client-sso": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.899.0.tgz", + "integrity": "sha512-EKz/iiVDv2OC8/3ONcXG3+rhphx9Heh7KXQdsZzsAXGVn6mWtrHQLrWjgONckmK4LrD07y4+5WlJlGkMxSMA5A==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.888.0", - "@aws-sdk/middleware-host-header": "3.887.0", - "@aws-sdk/middleware-logger": "3.887.0", - "@aws-sdk/middleware-recursion-detection": "3.887.0", - "@aws-sdk/middleware-user-agent": "3.888.0", - "@aws-sdk/region-config-resolver": "3.887.0", - "@aws-sdk/types": "3.887.0", - "@aws-sdk/util-endpoints": "3.887.0", - "@aws-sdk/util-user-agent-browser": "3.887.0", - "@aws-sdk/util-user-agent-node": "3.888.0", - "@smithy/config-resolver": "^4.2.1", - "@smithy/core": "^3.11.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/middleware-host-header": "3.893.0", + "@aws-sdk/middleware-logger": "3.893.0", + "@aws-sdk/middleware-recursion-detection": "3.893.0", + "@aws-sdk/middleware-user-agent": "3.899.0", + "@aws-sdk/region-config-resolver": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/util-endpoints": "3.895.0", + "@aws-sdk/util-user-agent-browser": "3.893.0", + "@aws-sdk/util-user-agent-node": "3.899.0", + "@smithy/config-resolver": "^4.2.2", + "@smithy/core": "^3.13.0", "@smithy/fetch-http-handler": "^5.2.1", "@smithy/hash-node": "^4.1.1", "@smithy/invalid-dependency": "^4.1.1", "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.1", - "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-endpoint": "^4.2.5", + "@smithy/middleware-retry": "^4.3.1", "@smithy/middleware-serde": "^4.1.1", "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/node-http-handler": "^4.2.1", "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", "@smithy/util-base64": "^4.1.0", "@smithy/util-body-length-browser": "^4.1.0", "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.1", - "@smithy/util-defaults-mode-node": "^4.1.1", - "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-defaults-mode-browser": "^4.1.5", + "@smithy/util-defaults-mode-node": "^4.1.5", + "@smithy/util-endpoints": "^3.1.2", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.1", + "@smithy/util-retry": "^4.1.2", "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, @@ -392,43 +562,41 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/core": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.888.0.tgz", - "integrity": "sha512-L3S2FZywACo4lmWv37Y4TbefuPJ1fXWyWwIJ3J4wkPYFJ47mmtUPqThlVrSbdTHkEjnZgJe5cRfxk0qCLsFh1w==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/core": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.899.0.tgz", + "integrity": "sha512-Enp5Zw37xaRlnscyaelaUZNxVqyE3CTS8gjahFbW2bbzVtRD2itHBVgq8A3lvKiFb7Feoxa71aTe0fQ1I6AhQQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", - "@aws-sdk/xml-builder": "3.887.0", - "@smithy/core": "^3.11.0", - "@smithy/node-config-provider": "^4.2.1", - "@smithy/property-provider": "^4.0.5", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/xml-builder": "3.894.0", + "@smithy/core": "^3.13.0", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/property-provider": "^4.1.1", "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.6.1", + "@smithy/signature-v4": "^5.2.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", "@smithy/util-middleware": "^4.1.1", "@smithy/util-utf8": "^4.1.0", - "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.888.0.tgz", - "integrity": "sha512-shPi4AhUKbIk7LugJWvNpeZA8va7e5bOHAEKo89S0Ac8WDZt2OaNzbh/b9l0iSL2eEyte8UgIsYGcFxOwIF1VA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.899.0.tgz", + "integrity": "sha512-wXQ//KQ751EFhUbdfoL/e2ZDaM8l2Cff+hVwFcj32yiZyeCMhnoLRMQk2euAaUOugqPY5V5qesFbHhISbIedtw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/property-provider": "^4.0.5", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -436,46 +604,46 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.888.0.tgz", - "integrity": "sha512-Jvuk6nul0lE7o5qlQutcqlySBHLXOyoPtiwE6zyKbGc7RVl0//h39Lab7zMeY2drMn8xAnIopL4606Fd8JI/Hw==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.899.0.tgz", + "integrity": "sha512-/rRHyJFdnPrupjt/1q/PxaO6O26HFsguVUJSUeMeGUWLy0W8OC3slLFDNh89CgTqnplCyt1aLFMCagRM20HjNQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/types": "3.887.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/types": "3.893.0", "@smithy/fetch-http-handler": "^5.2.1", "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.0.5", + "@smithy/property-provider": "^4.1.1", "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.1", + "@smithy/util-stream": "^4.3.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.888.0.tgz", - "integrity": "sha512-M82ItvS5yq+tO6ZOV1ruaVs2xOne+v8HW85GFCXnz8pecrzYdgxh6IsVqEbbWruryG/mUGkWMbkBZoEsy4MgyA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.899.0.tgz", + "integrity": "sha512-B8oFNFTDV0j1yiJiqzkC2ybml+theNnmsLrTLBhJbnBLWkxEcmVGKVIMnATW9BUCBhHmEtDiogdNIzSwP8tbMw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/credential-provider-env": "3.888.0", - "@aws-sdk/credential-provider-http": "3.888.0", - "@aws-sdk/credential-provider-process": "3.888.0", - "@aws-sdk/credential-provider-sso": "3.888.0", - "@aws-sdk/credential-provider-web-identity": "3.888.0", - "@aws-sdk/nested-clients": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/credential-provider-env": "3.899.0", + "@aws-sdk/credential-provider-http": "3.899.0", + "@aws-sdk/credential-provider-process": "3.899.0", + "@aws-sdk/credential-provider-sso": "3.899.0", + "@aws-sdk/credential-provider-web-identity": "3.899.0", + "@aws-sdk/nested-clients": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -483,23 +651,23 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.888.0.tgz", - "integrity": "sha512-KCrQh1dCDC8Y+Ap3SZa6S81kHk+p+yAaOQ5jC3dak4zhHW3RCrsGR/jYdemTOgbEGcA6ye51UbhWfrrlMmeJSA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.899.0.tgz", + "integrity": "sha512-nHBnZ2ZCOqTGJ2A9xpVj8iK6+WV+j0JNv3XGEkIuL4mqtGEPJlEex/0mD/hqc1VF8wZzojji2OQ3892m1mUOSA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.888.0", - "@aws-sdk/credential-provider-http": "3.888.0", - "@aws-sdk/credential-provider-ini": "3.888.0", - "@aws-sdk/credential-provider-process": "3.888.0", - "@aws-sdk/credential-provider-sso": "3.888.0", - "@aws-sdk/credential-provider-web-identity": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", + "@aws-sdk/credential-provider-env": "3.899.0", + "@aws-sdk/credential-provider-http": "3.899.0", + "@aws-sdk/credential-provider-ini": "3.899.0", + "@aws-sdk/credential-provider-process": "3.899.0", + "@aws-sdk/credential-provider-sso": "3.899.0", + "@aws-sdk/credential-provider-web-identity": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -507,17 +675,17 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.888.0.tgz", - "integrity": "sha512-+aX6piSukPQ8DUS4JAH344GePg8/+Q1t0+kvSHAZHhYvtQ/1Zek3ySOJWH2TuzTPCafY4nmWLcQcqvU1w9+4Lw==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.899.0.tgz", + "integrity": "sha512-1PWSejKcJQUKBNPIqSHlEW4w8vSjmb+3kNJqCinJybjp5uP5BJgBp6QNcb8Nv30VBM0bn3ajVd76LCq4ZshQAw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -525,19 +693,19 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.888.0.tgz", - "integrity": "sha512-b1ZJji7LJ6E/j1PhFTyvp51in2iCOQ3VP6mj5H6f5OUnqn7efm41iNMoinKr87n0IKZw7qput5ggXVxEdPhouA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.899.0.tgz", + "integrity": "sha512-URlMbo74CAhIGrhzEP2fw5F5Tt6MRUctA8aa88MomlEHCEbJDsMD3nh6qoXxwR3LyvEBFmCWOZ/1TWmAjMsSdA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.888.0", - "@aws-sdk/core": "3.888.0", - "@aws-sdk/token-providers": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", + "@aws-sdk/client-sso": "3.899.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/token-providers": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -545,17 +713,18 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.888.0.tgz", - "integrity": "sha512-7P0QNtsDzMZdmBAaY/vY1BsZHwTGvEz3bsn2bm5VSKFAeMmZqsHK1QeYdNsFjLtegnVh+wodxMq50jqLv3LFlA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.899.0.tgz", + "integrity": "sha512-UEn5o5FMcbeFPRRkJI6VCrgdyR9qsLlGA7+AKCYuYADsKbvJGIIQk6A2oD82vIVvLYD3TtbTLDLsF7haF9mpbw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/nested-clients": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/property-provider": "^4.0.5", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/nested-clients": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -563,14 +732,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.887.0.tgz", - "integrity": "sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.893.0.tgz", + "integrity": "sha512-qL5xYRt80ahDfj9nDYLhpCNkDinEXvjLe/Qen/Y/u12+djrR2MB4DRa6mzBCkLkdXDtf0WAoW2EZsNCfGrmOEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", + "@aws-sdk/types": "3.893.0", "@smithy/protocol-http": "^5.2.1", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" @@ -579,14 +748,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.887.0.tgz", - "integrity": "sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-logger": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.893.0.tgz", + "integrity": "sha512-ZqzMecjju5zkBquSIfVfCORI/3Mge21nUY4nWaGQy+NUXehqCGG4W7AiVpiHGOcY2cGJa7xeEkYcr2E2U9U0AA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", + "@aws-sdk/types": "3.893.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -594,14 +763,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.887.0.tgz", - "integrity": "sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.893.0.tgz", + "integrity": "sha512-H7Zotd9zUHQAr/wr3bcWHULYhEeoQrF54artgsoUGIf/9emv6LzY89QUccKIxYd6oHKNTrTyXm9F0ZZrzXNxlg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", + "@aws-sdk/types": "3.893.0", "@aws/lambda-invoke-store": "^0.0.1", "@smithy/protocol-http": "^5.2.1", "@smithy/types": "^4.5.0", @@ -611,25 +780,25 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.888.0.tgz", - "integrity": "sha512-rKOFNfqgqOfrdcLGF8fcO75azWS2aq2ksRHFoIEFru5FJxzu/yDAhY4C2FKiP/X34xeIUS2SbE/gQgrgWHSN2g==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.899.0.tgz", + "integrity": "sha512-/3/EIRSwQ5CNOSTHx96gVGzzmTe46OxcPG5FTgM6i9ZD+K/Q3J/UPGFL5DPzct5fXiSLvD1cGQitWHStVDjOVQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@aws-sdk/util-arn-parser": "3.873.0", - "@smithy/core": "^3.11.0", - "@smithy/node-config-provider": "^4.2.1", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.13.0", + "@smithy/node-config-provider": "^4.2.2", "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.6.1", + "@smithy/signature-v4": "^5.2.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-config-provider": "^4.1.0", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.1", + "@smithy/util-stream": "^4.3.2", "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, @@ -637,17 +806,17 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.888.0.tgz", - "integrity": "sha512-ZkcUkoys8AdrNNG7ATjqw2WiXqrhTvT+r4CIK3KhOqIGPHX0p0DQWzqjaIl7ZhSUToKoZ4Ud7MjF795yUr73oA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.899.0.tgz", + "integrity": "sha512-6EsVCC9j1VIyVyLOg+HyO3z9L+c0PEwMiHe3kuocoMf8nkfjSzJfIl6zAtgAXWgP5MKvusTP2SUbS9ezEEHZ+A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@aws-sdk/util-endpoints": "3.887.0", - "@smithy/core": "^3.11.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/util-endpoints": "3.895.0", + "@smithy/core": "^3.13.0", "@smithy/protocol-http": "^5.2.1", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" @@ -656,49 +825,49 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.888.0.tgz", - "integrity": "sha512-py4o4RPSGt+uwGvSBzR6S6cCBjS4oTX5F8hrHFHfPCdIOMVjyOBejn820jXkCrcdpSj3Qg1yUZXxsByvxc9Lyg==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/nested-clients": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.899.0.tgz", + "integrity": "sha512-ySXXsFO0RH28VISEqvCuPZ78VAkK45/+OCIJgPvYpcCX9CVs70XSvMPXDI46I49mudJ1s4H3IUKccYSEtA+jaw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.888.0", - "@aws-sdk/middleware-host-header": "3.887.0", - "@aws-sdk/middleware-logger": "3.887.0", - "@aws-sdk/middleware-recursion-detection": "3.887.0", - "@aws-sdk/middleware-user-agent": "3.888.0", - "@aws-sdk/region-config-resolver": "3.887.0", - "@aws-sdk/types": "3.887.0", - "@aws-sdk/util-endpoints": "3.887.0", - "@aws-sdk/util-user-agent-browser": "3.887.0", - "@aws-sdk/util-user-agent-node": "3.888.0", - "@smithy/config-resolver": "^4.2.1", - "@smithy/core": "^3.11.0", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/middleware-host-header": "3.893.0", + "@aws-sdk/middleware-logger": "3.893.0", + "@aws-sdk/middleware-recursion-detection": "3.893.0", + "@aws-sdk/middleware-user-agent": "3.899.0", + "@aws-sdk/region-config-resolver": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@aws-sdk/util-endpoints": "3.895.0", + "@aws-sdk/util-user-agent-browser": "3.893.0", + "@aws-sdk/util-user-agent-node": "3.899.0", + "@smithy/config-resolver": "^4.2.2", + "@smithy/core": "^3.13.0", "@smithy/fetch-http-handler": "^5.2.1", "@smithy/hash-node": "^4.1.1", "@smithy/invalid-dependency": "^4.1.1", "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.1", - "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-endpoint": "^4.2.5", + "@smithy/middleware-retry": "^4.3.1", "@smithy/middleware-serde": "^4.1.1", "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/node-http-handler": "^4.2.1", "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", "@smithy/util-base64": "^4.1.0", "@smithy/util-body-length-browser": "^4.1.0", "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.1", - "@smithy/util-defaults-mode-node": "^4.1.1", - "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-defaults-mode-browser": "^4.1.5", + "@smithy/util-defaults-mode-node": "^4.1.5", + "@smithy/util-endpoints": "^3.1.2", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.1", + "@smithy/util-retry": "^4.1.2", "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, @@ -706,17 +875,17 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.887.0.tgz", - "integrity": "sha512-VdSMrIqJ3yjJb/fY+YAxrH/lCVv0iL8uA+lbMNfQGtO5tB3Zx6SU9LEpUwBNX8fPK1tUpI65CNE4w42+MY/7Mg==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.893.0.tgz", + "integrity": "sha512-/cJvh3Zsa+Of0Zbg7vl9wp/kZtdb40yk/2+XcroAMVPO9hPvmS9r/UOm6tO7FeX4TtkRFwWaQJiTZTgSdsPY+Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", - "@smithy/node-config-provider": "^4.2.1", + "@aws-sdk/types": "3.893.0", + "@smithy/node-config-provider": "^4.2.2", "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-config-provider": "^4.1.0", "@smithy/util-middleware": "^4.1.1", "tslib": "^2.6.2" }, @@ -724,17 +893,17 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.888.0.tgz", - "integrity": "sha512-FmOHUaJzEhqfcpyh0L7HLwYcYopK13Dbmuf+oUyu56/RoeB1nLnltH1VMQVj8v3Am2IwlGR+/JpFyrdkErN+cA==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.899.0.tgz", + "integrity": "sha512-wV51Jogxhd7dI4Q2Y1ASbkwTsRT3G8uwWFDCwl+WaErOQAzofKlV6nFJQlfgjMk4iEn2gFOIWqJ8fMTGShRK/A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.888.0", - "@aws-sdk/types": "3.887.0", + "@aws-sdk/middleware-sdk-s3": "3.899.0", + "@aws-sdk/types": "3.893.0", "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.1.3", + "@smithy/signature-v4": "^5.2.1", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -742,18 +911,18 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.888.0.tgz", - "integrity": "sha512-WA3NF+3W8GEuCMG1WvkDYbB4z10G3O8xuhT7QSjhvLYWQ9CPt3w4VpVIfdqmUn131TCIbhCzD0KN/1VJTjAjyw==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/token-providers": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.899.0.tgz", + "integrity": "sha512-Ovu1nWr8HafYa/7DaUvvPnzM/yDUGDBqaiS7rRzv++F5VwyFY37+z/mHhvRnr+PbNWo8uf22a121SNue5uwP2w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.888.0", - "@aws-sdk/nested-clients": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", + "@aws-sdk/core": "3.899.0", + "@aws-sdk/nested-clients": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -761,10 +930,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/types": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.887.0.tgz", - "integrity": "sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/types": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.893.0.tgz", + "integrity": "sha512-Aht1nn5SnA0N+Tjv0dzhAY7CQbxVtmq1bBR6xI0MhG7p2XYVh1wXuKTzrldEvQWwA3odOYunAfT9aBiKZx9qIg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -775,10 +944,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz", - "integrity": "sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -788,59 +957,46 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.887.0.tgz", - "integrity": "sha512-kpegvT53KT33BMeIcGLPA65CQVxLUL/C3gTz9AzlU/SDmeusBHX4nRApAicNzI/ltQ5lxZXbQn18UczzBuwF1w==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-endpoints": { + "version": "3.895.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.895.0.tgz", + "integrity": "sha512-MhxBvWbwxmKknuggO2NeMwOVkHOYL98pZ+1ZRI5YwckoCL3AvISMnPJgfN60ww6AIXHGpkp+HhpFdKOe8RHSEg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.887.0", + "@aws-sdk/types": "3.893.0", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", - "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-endpoints": "^3.1.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", - "integrity": "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.893.0.tgz", + "integrity": "sha512-PE9NtbDBW6Kgl1bG6A5fF3EPo168tnkj8TgMcT0sg4xYBWsBpq0bpJZRh+Jm5Bkwiw9IgTCLjEU7mR6xWaMB9w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.887.0.tgz", - "integrity": "sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.887.0", + "@aws-sdk/types": "3.893.0", "@smithy/types": "^4.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.888.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.888.0.tgz", - "integrity": "sha512-rSB3OHyuKXotIGfYEo//9sU0lXAUrTY28SUUnxzOGYuQsAt0XR5iYwBAp+RjV6x8f+Hmtbg0PdCsy1iNAXa0UQ==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.899.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.899.0.tgz", + "integrity": "sha512-CiP0UAVQWLg2+8yciUBzVLaK5Fr7jBQ7wVu+p/O2+nlCOD3E3vtL1KZ1qX/d3OVpVSVaMAdZ9nbyewGV9hvjjg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.888.0", - "@aws-sdk/types": "3.887.0", - "@smithy/node-config-provider": "^4.2.1", + "@aws-sdk/middleware-user-agent": "3.899.0", + "@aws-sdk/types": "3.893.0", + "@smithy/node-config-provider": "^4.2.2", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -856,14 +1012,633 @@ } } }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.887.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.887.0.tgz", - "integrity": "sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ==", + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/xml-builder": { + "version": "3.894.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.894.0.tgz", + "integrity": "sha512-E6EAMc9dT1a2DOdo4zyOf3fp5+NJ2wI+mcm7RaW1baFIWDwcb99PpvWoV7YEiK7oaBDshuOEGWKUSYXdW+JYgA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", + "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", + "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", + "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", + "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", + "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", + "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-ini": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", + "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", + "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.835.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/token-providers": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", + "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.830.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.830.0.tgz", + "integrity": "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", + "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.835.0.tgz", + "integrity": "sha512-9ezorQYlr5cQY28zWAReFhNKUTaXsi3TMvXIagMRrSeWtQ7R6TCYnt91xzHRCmFR2kp3zLI+dfoeH+wF3iCKUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", + "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.835.0.tgz", + "integrity": "sha512-oPebxpVf9smInHhevHh3APFZagGU+4RPwXEWv9YtYapFvsMq+8QXFvOfxfVZ/mwpe0JVG7EiJzL9/9Kobmts8Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", + "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", + "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@smithy/core": "^3.5.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", + "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.835.0.tgz", + "integrity": "sha512-rEtJH4dIwJYlXXe5rIH+uTCQmd2VIjuaoHlDY3Dr4nxF6po6U7vKsLfybIU2tgflGVqoqYQnXsfW/kj/Rh+/ow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", + "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", + "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", + "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -895,15 +1670,66 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -912,6 +1738,33 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -922,6 +1775,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -942,14 +1827,38 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -974,18 +1883,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -993,9 +1902,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1016,12 +1925,12 @@ } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.5.tgz", + "integrity": "sha512-yKBJUnt9U2uuSZ0i1+Uh4ifeQBqqVgPC2jux99ixYW8n63f5d3O/HvsHiJm++idfKvRYsdbQHQ4tfkR3fTHHow==", "license": "MIT", "dependencies": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.4", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -1073,20 +1982,20 @@ } }, "node_modules/@emnapi/core": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", - "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", + "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.4", + "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", "license": "MIT", "optional": true, "dependencies": { @@ -1094,9 +2003,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", - "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", "license": "MIT", "optional": true, "dependencies": { @@ -2138,9 +3047,9 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", - "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.7.3", @@ -2148,12 +3057,12 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", - "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.3" + "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", @@ -2236,6 +3145,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "license": "MIT", "dependencies": { "@standard-schema/utils": "^0.3.0" }, @@ -2253,31 +3163,18 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2304,13 +3201,24 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", + "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2323,16 +3231,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" + "@img/sharp-libvips-darwin-arm64": "1.1.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", + "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2345,16 +3254,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" + "@img/sharp-libvips-darwin-x64": "1.1.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2365,12 +3275,13 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2381,12 +3292,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", "cpu": [ "arm" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2397,12 +3309,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2413,12 +3326,13 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", "cpu": [ "ppc64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2429,12 +3343,13 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", "cpu": [ "s390x" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2445,12 +3360,13 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2461,12 +3377,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2477,12 +3394,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2493,12 +3411,13 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", + "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2511,16 +3430,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" + "@img/sharp-libvips-linux-arm": "1.1.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", + "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2533,13 +3453,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" + "@img/sharp-libvips-linux-arm64": "1.1.0" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", "cpu": [ "ppc64" ], @@ -2555,16 +3475,33 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-ppc64/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", + "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2577,16 +3514,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" + "@img/sharp-libvips-linux-s390x": "1.1.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", + "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2599,16 +3537,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" + "@img/sharp-libvips-linux-x64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", + "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2621,16 +3560,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", + "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2643,20 +3583,21 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", + "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", "cpu": [ "wasm32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.4" + "@emnapi/runtime": "^1.4.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -2666,9 +3607,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", + "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", "cpu": [ "arm64" ], @@ -2685,12 +3626,13 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", + "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -2704,12 +3646,13 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", + "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -2722,6 +3665,12 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@ioredis/commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", + "license": "MIT" + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -2764,7 +3713,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.4" @@ -2805,6 +3753,17 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -2813,9 +3772,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -2829,6 +3788,26 @@ "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==", "license": "MIT" }, + "node_modules/@lottiefiles/dotlottie-react": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.13.3.tgz", + "integrity": "sha512-V4FfdYlqzjBUX7f0KV6vfQOOI0Cp+3XeG/ZqSDFSEVg5P7fpROpDv5/I9aTM8sOCESK1SWT96Fem+QVUnBV1wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lottiefiles/dotlottie-web": "0.42.0" + }, + "peerDependencies": { + "react": "^17 || ^18 || ^19" + } + }, + "node_modules/@lottiefiles/dotlottie-web": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.42.0.tgz", + "integrity": "sha512-Zr2LCaOAoPCsdAQgeLyCSiQ1+xrAJtRCyuEYDj0qR5heUwpc+Pxbb88JyTVumcXFfKOBMOMmrlsTScLz2mrvQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2998,9 +3977,9 @@ } }, "node_modules/@noble/curves": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.6.tgz", - "integrity": "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", "dev": true, "license": "MIT", "dependencies": { @@ -3652,12 +4631,12 @@ "license": "MIT" }, "node_modules/@peculiar/asn1-android": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.4.0.tgz", - "integrity": "sha512-YFueREq97CLslZZBI8dKzis7jMfEHSLxM+nr0Zdx1POiXFLjqqwoY5s0F1UimdBiEw/iKlHey2m56MRDv7Jtyg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.5.0.tgz", + "integrity": "sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.4.0", + "@peculiar/asn1-schema": "^2.5.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } @@ -3807,10 +4786,28 @@ "tsyringe": "^4.10.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@posthog/core": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.0.2.tgz", - "integrity": "sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.2.2.tgz", + "integrity": "sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==", + "license": "MIT" + }, + "node_modules/@radix-ui/colors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", + "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==", + "dev": true, "license": "MIT" }, "node_modules/@radix-ui/number": { @@ -4663,6 +5660,221 @@ } } }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.6.tgz", + "integrity": "sha512-3SeJxKeO3TO1zVw1Nl++Cp0krYk6zHDHMCUXXVkosIzl6Nxcvb07EerQpyD2wXQSJ5RZajrYAmPaydU8Hk1IyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.6.tgz", + "integrity": "sha512-XOBq9VqC+mIn5hzjGdJLhQbvQeiOpV5ExNE6qMQQPvFsCT44QUcxFzYytTWVoyWg9XKfgrleKmTeEyu6aoTPhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-roving-focus": "1.1.6", + "@radix-ui/react-toggle": "1.1.6", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-collection": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", + "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.6.tgz", + "integrity": "sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", @@ -4974,6 +6186,24 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, + "node_modules/@react-email/components/node_modules/@react-email/render": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.2.3.tgz", + "integrity": "sha512-qu3XYNkHGao3teJexVD5CrcgFkNLrzbZvpZN17a7EyQYUN3kHkTkE9saqY4VbvGx6QoNU3p8rsk/Xm++D/+pTw==", + "license": "MIT", + "dependencies": { + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, "node_modules/@react-email/container": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", @@ -5094,10 +6324,826 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, - "node_modules/@react-email/render": { + "node_modules/@react-email/preview-server": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.1.0.tgz", + "integrity": "sha512-wz4dQyQtIjAavJ0bVIu+fkZMUUmfYkKGYAwFFD6YvNdNwhU+HdhxfzdCJ1zwOjkc4ETCGtA3rJIKQ41D3La3jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.26.10", + "@babel/parser": "^7.27.0", + "@babel/traverse": "^7.27.0", + "@lottiefiles/dotlottie-react": "0.13.3", + "@radix-ui/colors": "3.0.0", + "@radix-ui/react-collapsible": "1.1.7", + "@radix-ui/react-dropdown-menu": "2.1.10", + "@radix-ui/react-popover": "1.1.10", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-tabs": "1.1.7", + "@radix-ui/react-toggle-group": "1.1.6", + "@radix-ui/react-tooltip": "1.2.3", + "@types/node": "22.14.1", + "@types/normalize-path": "3.0.2", + "@types/react": "19.0.10", + "@types/react-dom": "19.0.4", + "@types/webpack": "5.28.5", + "autoprefixer": "10.4.21", + "chalk": "^4.1.2", + "clsx": "2.1.1", + "esbuild": "^0.25.0", + "framer-motion": "12.7.5", + "json5": "2.2.3", + "log-symbols": "^4.1.0", + "module-punycode": "npm:punycode@2.3.1", + "next": "^15.3.2", + "node-html-parser": "7.0.1", + "ora": "^5.4.1", + "pretty-bytes": "6.1.1", + "prism-react-renderer": "2.4.1", + "react": "19.0.0", + "react-dom": "19.0.0", + "sharp": "0.34.1", + "socket.io-client": "4.8.1", + "sonner": "2.0.3", + "source-map-js": "1.2.1", + "spamc": "0.0.5", + "stacktrace-parser": "0.1.11", + "tailwind-merge": "3.2.0", + "tailwindcss": "3.4.0", + "use-debounce": "10.0.4", + "zod": "3.24.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-arrow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", + "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-collapsible": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.7.tgz", + "integrity": "sha512-zGFsPcFJNdQa/UNd6MOgF40BS054FIGj32oOWBllixz42f+AkQg3QJ1YT9pw7vs+Ai+EgWkh839h69GEK8oH2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-collection": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", + "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", + "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.10.tgz", + "integrity": "sha512-8qnILty92BmXbxKugWX3jgEeFeMoxtdggeCCxb/aB7l34QFAKB23IhJfnwyVMbRnAUJiT5LOay4kUS22+AWuRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.10", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", + "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-menu": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.10.tgz", + "integrity": "sha512-OupA+1PrVf2H0K4jIwkDyA+rsJ7vF1y/VxLEO43dmZ68GtCjvx9K1/B/QscPZM3jIeFNK/wPd0HmiLjT36hVcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.4", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-roving-focus": "1.1.6", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-popover": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.10.tgz", + "integrity": "sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.4", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-popper": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", + "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-portal": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", + "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-presence": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", + "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.6.tgz", + "integrity": "sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-tabs": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.7.tgz", + "integrity": "sha512-sawt4HkD+6haVGjYOC3BMIiCumBpqTK6o407n6zN/6yReed2EN7bXyykNrpqg+xCfudpBUZg7Y2cJBd/x/iybA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-roving-focus": "1.1.6", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-tooltip": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.2.3.tgz", - "integrity": "sha512-qu3XYNkHGao3teJexVD5CrcgFkNLrzbZvpZN17a7EyQYUN3kHkTkE9saqY4VbvGx6QoNU3p8rsk/Xm++D/+pTw==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.3.tgz", + "integrity": "sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.4", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz", + "integrity": "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/react-dom": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@react-email/preview-server/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@react-email/preview-server/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/@react-email/preview-server/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@react-email/preview-server/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/@react-email/preview-server/node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/tailwind-merge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", + "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/@react-email/preview-server/node_modules/tailwindcss": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", + "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/zod": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@react-email/render": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.1.tgz", + "integrity": "sha512-BOc/kanieEVyiuldFFvceriiBGBBVhe4JWWXCXE2ehLIqz+gSWD4rgCoXAGbJRnnVyyp4joPqK62bSfa88yonA==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -5227,7 +7273,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5237,14 +7282,38 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/config-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.1.tgz", - "integrity": "sha512-FXil8q4QN7mgKwU2hCLm0ltab8NyY/1RiqEf25Jnf6WLS3wmb11zGAoLETqg1nur2Aoibun4w4MjeN9CMJ4G6A==", - "dev": true, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.1.0.tgz", + "integrity": "sha512-a36AtR7Q7XOhRPt6F/7HENmTWcB8kN7mDJcOFM/+FuKO6x88w8MQJfYCufMWh4fGyVkPjUh3Rrz/dnqFQdo6OQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.1.0.tgz", + "integrity": "sha512-Bnv0B3nSlfB2mPO0WgM49I/prl7+kamF042rrf3ezJ3Z4C7csPYvyYgZfXTGXwXfj1mAwDWjE/ybIf49PzFzvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", + "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.2.2", "@smithy/types": "^4.5.0", "@smithy/util-config-provider": "^4.1.0", "@smithy/util-middleware": "^4.1.1", @@ -5255,10 +7324,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.11.0.tgz", - "integrity": "sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA==", - "dev": true, + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.13.0.tgz", + "integrity": "sha512-BI6ALLPOKnPOU1Cjkc+1TPhOlP3JXSR/UH14JmnaLq41t3ma+IjuXrKfhycVjr5IQ0XxRh2NnQo3olp+eCVrGg==", "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^4.1.1", @@ -5267,38 +7335,22 @@ "@smithy/util-base64": "^4.1.0", "@smithy/util-body-length-browser": "^4.1.0", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.1", + "@smithy/util-stream": "^4.3.2", "@smithy/util-utf8": "^4.1.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@smithy/uuid": "^1.0.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/core/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.1.tgz", - "integrity": "sha512-1WdBfM9DwA59pnpIizxnUvBf/de18p4GP+6zP2AqrlFzoW3ERpZaT4QueBR0nS9deDMaQRkBlngpVlnkuuTisQ==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", + "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/property-provider": "^4.1.1", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", @@ -5308,11 +7360,80 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz", + "integrity": "sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.5.0", + "@smithy/util-hex-encoding": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz", + "integrity": "sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz", + "integrity": "sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz", + "integrity": "sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz", + "integrity": "sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/fetch-http-handler": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.2.1", @@ -5325,11 +7446,25 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.1.1.tgz", + "integrity": "sha512-avAtk++s1e/1VODf+rg7c9R2pB5G9y8yaJaGY4lPZI2+UIqVyuSDMikWjeWfBVmFZ3O7NpDxBbUCyGhThVUKWQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.1.0", + "@smithy/chunked-blob-reader-native": "^4.1.0", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/hash-node": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5341,11 +7476,24 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.1.1.tgz", + "integrity": "sha512-3ztT4pV0Moazs3JAYFdfKk11kYFDo4b/3R3+xVjIm6wY9YpJf+xfz+ocEnNKcWAdcmSMqi168i2EMaKmJHbJMA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/invalid-dependency": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5359,7 +7507,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5368,11 +7515,24 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/md5-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.1.1.tgz", + "integrity": "sha512-MvWXKK743BuHjr/hnWuT6uStdKEaoqxHAQUvbKJPPZM5ZojTNFI5D+47BoQfBE5RgGlRRty05EbWA+NXDv+hIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/middleware-content-length": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.2.1", @@ -5384,16 +7544,15 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.1.tgz", - "integrity": "sha512-fUTMmQvQQZakXOuKizfu7fBLDpwvWZjfH6zUK2OLsoNZRZGbNUdNSdLJHpwk1vS208jtDjpUIskh+JoA8zMzZg==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.5.tgz", + "integrity": "sha512-DdOIpssQ5LFev7hV6GX9TMBW5ChTsQBxqgNW1ZGtJNSAi5ksd5klwPwwMY0ejejfEzwXXGqxgVO3cpaod4veiA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.11.0", + "@smithy/core": "^3.13.0", "@smithy/middleware-serde": "^4.1.1", - "@smithy/node-config-provider": "^4.2.1", - "@smithy/shared-ini-file-loader": "^4.1.1", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "@smithy/url-parser": "^4.1.1", "@smithy/util-middleware": "^4.1.1", @@ -5404,46 +7563,29 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.2.1.tgz", - "integrity": "sha512-JzfvjwSJXWRl7LkLgIRTUTd2Wj639yr3sQGpViGNEOjtb0AkAuYqRAHs+jSOI/LPC0ZTjmFVVtfrCICMuebexw==", - "dev": true, + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.3.1.tgz", + "integrity": "sha512-aH2bD1bzb6FB04XBhXA5mgedEZPKx3tD/qBuYCAKt5iieWvWO1Y2j++J9uLqOndXb9Pf/83Xka/YjSnMbcPchA==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/protocol-http": "^5.2.1", - "@smithy/service-error-classification": "^4.1.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/service-error-classification": "^4.1.2", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.1", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@smithy/util-retry": "^4.1.2", + "@smithy/uuid": "^1.0.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@smithy/middleware-serde": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.2.1", @@ -5458,7 +7600,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5469,14 +7610,13 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.1.tgz", - "integrity": "sha512-AIA0BJZq2h295J5NeCTKhg1WwtdTA/GqBCaVjk30bDgMHwniUETyh5cP9IiE9VrId7Kt8hS7zvREVMTv1VfA6g==", - "dev": true, + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", + "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -5488,7 +7628,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.1.1", @@ -5505,7 +7644,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5519,7 +7657,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5533,7 +7670,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5548,7 +7684,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5559,10 +7694,9 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.1.tgz", - "integrity": "sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.2.tgz", + "integrity": "sha512-Kqd8wyfmBWHZNppZSMfrQFpc3M9Y/kjyN8n8P4DqJJtuwgK1H914R471HTw7+RL+T7+kI1f1gOnL7Vb5z9+NgQ==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0" @@ -5572,10 +7706,9 @@ } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.1.1.tgz", - "integrity": "sha512-YkpikhIqGc4sfXeIbzSj10t2bJI/sSoP5qxLue6zG+tEE3ngOBSm8sO3+djacYvS/R5DfpxN/L9CyZsvwjWOAQ==", - "dev": true, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", + "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5589,7 +7722,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.1.0", @@ -5606,18 +7738,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.1.tgz", - "integrity": "sha512-WolVLDb9UTPMEPPOncrCt6JmAMCSC/V2y5gst2STWJ5r7+8iNac+EFYQnmvDCYMfOLcilOSEpm5yXZXwbLak1Q==", - "dev": true, + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.5.tgz", + "integrity": "sha512-6J2hhuWu7EjnvLBIGltPCqzNswL1cW/AkaZx6i56qLsQ0ix17IAhmDD9aMmL+6CN9nCJODOXpBTCQS6iKAA7/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.11.0", - "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/core": "^3.13.0", + "@smithy/middleware-endpoint": "^4.2.5", "@smithy/middleware-stack": "^4.1.1", "@smithy/protocol-http": "^5.2.1", "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.1", + "@smithy/util-stream": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5628,7 +7759,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5641,7 +7771,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/querystring-parser": "^4.1.1", @@ -5656,7 +7785,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.1.0", @@ -5671,7 +7799,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5684,7 +7811,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5697,7 +7823,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.1.0", @@ -5711,7 +7836,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5721,14 +7845,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.1.tgz", - "integrity": "sha512-hA1AKIHFUMa9Tl6q6y8p0pJ9aWHCCG8s57flmIyLE0W7HcJeYrYtnqXDcGnftvXEhdQnSexyegXnzzTGk8bKLA==", - "dev": true, + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.5.tgz", + "integrity": "sha512-FGBhlmFZVSRto816l6IwrmDcQ9pUYX6ikdR1mmAhdtSS1m77FgADukbQg7F7gurXfAvloxE/pgsrb7SGja6FQA==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -5738,17 +7861,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.1.tgz", - "integrity": "sha512-RGSpmoBrA+5D2WjwtK7tto6Pc2wO9KSXKLpLONhFZ8VyuCbqlLdiDAfuDTNY9AJe4JoE+Cx806cpTQQoQ71zPQ==", - "dev": true, + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.5.tgz", + "integrity": "sha512-Gwj8KLgJ/+MHYjVubJF0EELEh9/Ir7z7DFqyYlwgmp4J37KE+5vz6b3pWUnSt53tIe5FjDfVjDmHGYKjwIvW0Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.2.1", - "@smithy/credential-provider-imds": "^4.1.1", - "@smithy/node-config-provider": "^4.2.1", + "@smithy/config-resolver": "^4.2.2", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/node-config-provider": "^4.2.2", "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.1", + "@smithy/smithy-client": "^4.6.5", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -5757,13 +7879,12 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.1.tgz", - "integrity": "sha512-qB4R9kO0SetA11Rzu6MVGFIaGYX3p6SGGGfWwsKnC6nXIf0n/0AKVwRTsYsz9ToN8CeNNtNgQRwKFBndGJZdyw==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", + "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-config-provider": "^4.2.2", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -5775,7 +7896,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5788,7 +7908,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.5.0", @@ -5799,13 +7918,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.1.tgz", - "integrity": "sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.2.tgz", + "integrity": "sha512-NCgr1d0/EdeP6U5PSZ9Uv5SMR5XRRYoVr1kRVtKZxWL3tixEL3UatrPIMFZSKwHlCcp2zPLDvMubVDULRqeunA==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.1.1", + "@smithy/service-error-classification": "^4.1.2", "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, @@ -5814,10 +7932,9 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.1.tgz", - "integrity": "sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA==", - "dev": true, + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.2.tgz", + "integrity": "sha512-Ka+FA2UCC/Q1dEqUanCdpqwxOFdf5Dg2VXtPtB1qxLcSGh5C1HdzklIt18xL504Wiy9nNUKwDMRTVCbKGoK69g==", "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.2.1", @@ -5837,7 +7954,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5850,7 +7966,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.1.0", @@ -5860,6 +7975,88 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/util-waiter": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.1.1.tgz", + "integrity": "sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.0.0.tgz", + "integrity": "sha512-OlA/yZHh0ekYFnbUkmYBDQPE6fGfdrvgz39ktp8Xf+FA6BfxLejPTMDOG0Nfk5/rDySAz1dRbFf24zaAFYVXlQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.5.tgz", + "integrity": "sha512-m2yI81kZ+2J2psTYtmBs3lfl/LM4WgLMCzkweMfQdbbyndcyOmFa6pblHjvjfblphPaGDzTDK+lmC/dUMSnv0A==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-convert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@so-ric/colorspace/node_modules/color-string": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -6122,66 +8319,6 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.4.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.4", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.4.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.0", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", @@ -6264,9 +8401,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", - "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "license": "MIT", "optional": true, "dependencies": { @@ -6331,6 +8468,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -6451,6 +8610,13 @@ "@types/node": "*" } }, + "node_modules/@types/normalize-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/normalize-path/-/normalize-path-3.0.2.tgz", + "integrity": "sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/pg": { "version": "8.15.5", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", @@ -6463,6 +8629,13 @@ "pg-types": "^2.2.0" } }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", @@ -6548,9 +8721,20 @@ "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true, "license": "MIT" }, + "node_modules/@types/webpack": { + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", + "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -6579,16 +8763,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", - "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/type-utils": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -6602,7 +8786,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -6617,15 +8801,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", - "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "engines": { @@ -6641,13 +8825,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", - "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.1", - "@typescript-eslint/types": "^8.44.1", + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", "debug": "^4.3.4" }, "engines": { @@ -6662,13 +8846,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", - "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6679,9 +8863,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", - "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6695,14 +8879,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", - "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6719,9 +8903,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", - "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6732,15 +8916,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", - "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.1", - "@typescript-eslint/tsconfig-utils": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6812,15 +8996,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", - "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1" + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6835,12 +9019,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", - "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/types": "8.45.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -7101,6 +9285,181 @@ "win32" ] }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -7126,6 +9485,19 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -7151,10 +9523,52 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { "node": ">=12" @@ -7178,6 +9592,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -7216,6 +9637,13 @@ "@oslojs/jwt": "0.2.0" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -7452,12 +9880,59 @@ "node": ">= 0.4" } }, + "node_modules/async-generator-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-generator-function/-/async-generator-function-1.0.0.tgz", + "integrity": "sha512-+NAXNqgCrB95ya4Sr66i1CL2hqLVckAk7xwRYWdcm39/ELQ6YNn1aw5r0bdQtqNZgQpEWzc5yc/igXc7aL5SLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -7538,6 +10013,16 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/better-sqlite3": { "version": "11.7.0", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.7.0.tgz", @@ -7602,11 +10087,17 @@ "node": ">=18" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/bowser": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", - "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { @@ -7631,6 +10122,40 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -7733,10 +10258,20 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001734", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", - "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", + "version": "1.0.30001745", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", + "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", "funding": [ { "type": "opencollective", @@ -7799,12 +10334,21 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -7828,19 +10372,16 @@ } }, "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^5.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/cli-spinners": { @@ -7877,9 +10418,9 @@ } }, "node_modules/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { "node": ">=12" @@ -7889,9 +10430,9 @@ } }, "node_modules/cliui/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { @@ -7912,9 +10453,9 @@ } }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", @@ -7946,6 +10487,15 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cmdk": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", @@ -7966,8 +10516,8 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -7998,47 +10548,13 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/colorspace/node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/colorspace/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/colorspace/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -8105,6 +10621,13 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -8209,6 +10732,49 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "license": "MIT" }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -8296,9 +10862,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -8357,6 +10923,29 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -8400,6 +10989,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -8410,9 +11008,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", + "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -8424,6 +11022,13 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -8437,6 +11042,13 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -8505,9 +11117,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -8711,6 +11323,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -8762,6 +11381,60 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -9008,6 +11681,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -9575,6 +12255,16 @@ "node": ">= 0.6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9739,20 +12429,40 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" @@ -9768,10 +12478,13 @@ } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -10028,6 +12741,48 @@ "node": ">= 0.6" } }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.7.5", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.5.tgz", + "integrity": "sha512-iD+vBOLn8E8bwBAFUQ1DYXjivm+cGGPgQUQ4Doleq7YP/zHdozUVwAMBJwOOfCTbtM8uOooMi77noD261Kxiyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-dom": "^12.7.5", + "motion-utils": "^12.7.5", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -10103,6 +12858,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.0.tgz", + "integrity": "sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -10113,9 +12887,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", "engines": { "node": ">=18" @@ -10125,16 +12899,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.1.tgz", + "integrity": "sha512-fk1ZVEeOX9hVZ6QzoBNEC55+Ucqg4sTVwrVuigZhuRPESVFpMyXnd3sbXvPOwp7Y9riVyANiqhEuRF0G1aVSeQ==", "license": "MIT", "dependencies": { + "async-function": "^1.0.0", + "async-generator-function": "^1.0.0", "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", + "generator-function": "^2.0.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", @@ -10253,6 +13030,13 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/glob/node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", @@ -10333,7 +13117,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -10429,6 +13212,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/helmet": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", @@ -10630,6 +13423,30 @@ "tslib": "^2.8.0" } }, + "node_modules/ioredis": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", + "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", @@ -10666,9 +13483,10 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -10867,16 +13685,13 @@ } }, "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/is-map": { @@ -11040,13 +13855,13 @@ } }, "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11105,7 +13920,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=16" @@ -11143,10 +13957,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz", + "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==", "devOptional": true, "license": "MIT", "bin": { @@ -11199,6 +14044,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -11212,15 +14063,16 @@ "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonwebtoken": { @@ -11597,6 +14449,33 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11612,12 +14491,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -11661,17 +14552,17 @@ "license": "MIT" }, "node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11707,12 +14598,13 @@ } }, "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", - "engines": { - "node": "20 || >=22" + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/lucide-react": { @@ -11755,6 +14647,20 @@ "node": ">= 0.4" } }, + "node_modules/maxmind": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-5.0.0.tgz", + "integrity": "sha512-ndhnbeQWKuiBU17BJ6cybUnvcyvNXaK+1VM5n9/I7+TIqAYFLDvX1DSoVfE1hgvZfudvAU9Ts1CW5sxYq/M8dA==", + "license": "MIT", + "dependencies": { + "mmdb-lib": "3.0.1", + "tiny-lru": "11.3.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/md-to-react-email": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/md-to-react-email/-/md-to-react-email-5.0.5.tgz", @@ -11948,10 +14854,9 @@ } }, "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -11960,28 +14865,33 @@ "node": ">= 18" } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, + "node_modules/mmdb-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.1.tgz", + "integrity": "sha512-dyAyMR+cRykZd1mw5altC9f4vKpCsuywPwo8l/L5fKqDay2zmqT0mF/BvUoXnQiqGn+nceO914rkPKJoyFnGxA==", + "license": "MIT", + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/module-punycode": { + "name": "punycode", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -11991,6 +14901,23 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "12.23.21", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", + "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -12011,6 +14938,18 @@ "url": "https://github.com/sponsors/raouldeheer" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -12065,6 +15004,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/next": { "version": "15.5.3", "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", @@ -12154,6 +15100,383 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/next/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/next/node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -12182,10 +15505,53 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/next/node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "version": "3.77.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", + "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -12244,6 +15610,24 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-html-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.0.1.tgz", + "integrity": "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, "node_modules/nodemailer": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz", @@ -12263,10 +15647,20 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.0.tgz", - "integrity": "sha512-d/P7DbvYgYNde9Ehfeq99+13/E7E82PfZPw8uYZASr9sQ3ZhBBCA9cXSJRA1COfJ6jDLJ0K36UJnXQWhCvLXuQ==", + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", + "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -12345,57 +15739,57 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.4", - "@npmcli/config": "^10.4.0", + "@npmcli/arborist": "^9.1.5", + "@npmcli/config": "^10.4.1", "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^4.0.2", - "@npmcli/package-json": "^6.2.0", - "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.1", + "@npmcli/promise-spawn": "^8.0.3", "@npmcli/redact": "^3.2.2", - "@npmcli/run-script": "^9.1.0", - "@sigstore/tuf": "^3.1.1", + "@npmcli/run-script": "^10.0.0", + "@sigstore/tuf": "^4.0.0", "abbrev": "^3.0.1", "archy": "~1.0.0", - "cacache": "^19.0.1", - "chalk": "^5.4.1", + "cacache": "^20.0.1", + "chalk": "^5.6.2", "ci-info": "^4.3.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.4.5", + "glob": "^11.0.3", "graceful-fs": "^4.2.11", - "hosted-git-info": "^8.1.0", + "hosted-git-info": "^9.0.0", "ini": "^5.0.0", - "init-package-json": "^8.2.1", - "is-cidr": "^5.1.1", + "init-package-json": "^8.2.2", + "is-cidr": "^6.0.0", "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.1", - "libnpmdiff": "^8.0.7", - "libnpmexec": "^10.1.6", - "libnpmfund": "^7.0.7", - "libnpmorg": "^8.0.0", - "libnpmpack": "^9.0.7", - "libnpmpublish": "^11.1.0", - "libnpmsearch": "^9.0.0", - "libnpmteam": "^8.0.1", - "libnpmversion": "^8.0.1", - "make-fetch-happen": "^14.0.3", - "minimatch": "^9.0.5", + "libnpmaccess": "^10.0.2", + "libnpmdiff": "^8.0.8", + "libnpmexec": "^10.1.7", + "libnpmfund": "^7.0.8", + "libnpmorg": "^8.0.1", + "libnpmpack": "^9.0.8", + "libnpmpublish": "^11.1.1", + "libnpmsearch": "^9.0.1", + "libnpmteam": "^8.0.2", + "libnpmversion": "^8.0.2", + "make-fetch-happen": "^15.0.2", + "minimatch": "^10.0.3", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^11.2.0", + "node-gyp": "^11.4.2", "nopt": "^8.1.0", - "normalize-package-data": "^7.0.1", + "normalize-package-data": "^8.0.0", "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.1", - "npm-package-arg": "^12.0.2", - "npm-pick-manifest": "^10.0.0", - "npm-profile": "^11.0.1", - "npm-registry-fetch": "^18.0.2", + "npm-install-checks": "^7.1.2", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-profile": "^12.0.0", + "npm-registry-fetch": "^19.0.0", "npm-user-validate": "^3.0.0", "p-map": "^7.0.3", - "pacote": "^21.0.0", + "pacote": "^21.0.3", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", @@ -12403,10 +15797,10 @@ "semver": "^7.7.2", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", - "supports-color": "^10.0.0", - "tar": "^6.2.1", + "supports-color": "^10.2.2", + "tar": "^7.5.1", "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", + "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^6.0.2", "which": "^5.0.0" @@ -12432,6 +15826,25 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -12449,7 +15862,7 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -12481,7 +15894,7 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.1.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -12511,55 +15924,54 @@ "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { - "version": "3.0.0", + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.4", + "version": "9.1.5", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^4.0.0", "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^3.0.0", "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.1", + "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^4.0.0", "@npmcli/redact": "^3.0.0", - "@npmcli/run-script": "^9.0.1", + "@npmcli/run-script": "^10.0.0", "bin-links": "^5.0.0", - "cacache": "^19.0.1", + "cacache": "^20.0.1", "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", - "lru-cache": "^10.2.2", - "minimatch": "^9.0.4", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", "nopt": "^8.0.0", "npm-install-checks": "^7.1.0", - "npm-package-arg": "^12.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.1", - "pacote": "^21.0.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "proggy": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", - "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", "ssri": "^12.0.0", "treeverse": "^3.0.0", @@ -12573,12 +15985,12 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.0", + "version": "10.4.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/package-json": "^6.0.1", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^5.0.0", "nopt": "^8.1.0", @@ -12602,21 +16014,21 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "6.0.3", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^8.0.0", "ini": "^5.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^10.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { @@ -12635,25 +16047,25 @@ } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "4.0.2", + "version": "5.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0" + "@npmcli/package-json": "^7.0.0", + "glob": "^11.0.3", + "minimatch": "^10.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "cacache": "^19.0.0", + "cacache": "^20.0.0", "json-parse-even-better-errors": "^4.0.0", "pacote": "^21.0.0", "proc-log": "^5.0.0", @@ -12680,24 +16092,24 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "6.2.0", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^8.0.0", + "@npmcli/git": "^7.0.0", + "glob": "^11.0.3", + "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^4.0.0", "proc-log": "^5.0.0", "semver": "^7.5.3", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.2", + "version": "8.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -12727,19 +16139,19 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "9.1.0", + "version": "10.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.0", + "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^8.0.0", "node-gyp": "^11.0.0", "proc-log": "^5.0.0", "which": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@pkgjs/parseargs": { @@ -12752,26 +16164,26 @@ } }, "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.0" + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/core": { - "version": "2.0.0", + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.4.3", + "version": "0.5.0", "inBundle": true, "license": "Apache-2.0", "engines": { @@ -12779,44 +16191,44 @@ } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "make-fetch-happen": "^14.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "3.1.1", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.1", - "tuf-js": "^3.0.1" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/verify": { - "version": "2.1.1", + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.1" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@tufjs/canonical-json": { @@ -12828,7 +16240,7 @@ } }, "node_modules/npm/node_modules/@tufjs/models": { - "version": "3.0.1", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -12836,7 +16248,21 @@ "minimatch": "^9.0.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/abbrev": { @@ -12864,7 +16290,7 @@ } }, "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.1", + "version": "6.2.3", "inBundle": true, "license": "MIT", "engines": { @@ -12924,86 +16350,28 @@ } }, "node_modules/npm/node_modules/cacache": { - "version": "19.0.1", + "version": "20.0.1", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^11.0.3", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", - "tar": "^7.4.3", "unique-filename": "^4.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/chownr": { - "version": "3.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/yallist": { - "version": "5.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/chalk": { - "version": "5.4.1", + "version": "5.6.2", "inBundle": true, "license": "MIT", "engines": { @@ -13014,11 +16382,11 @@ } }, "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", + "version": "3.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/npm/node_modules/ci-info": { @@ -13036,14 +16404,14 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "4.1.3", + "version": "5.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "ip-regex": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/npm/node_modules/cli-columns": { @@ -13100,6 +16468,11 @@ "node": ">= 8" } }, + "node_modules/npm/node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", "inBundle": true, @@ -13126,7 +16499,7 @@ } }, "node_modules/npm/node_modules/debug": { - "version": "4.4.1", + "version": "4.4.3", "inBundle": true, "license": "MIT", "dependencies": { @@ -13142,7 +16515,7 @@ } }, "node_modules/npm/node_modules/diff": { - "version": "7.0.0", + "version": "8.0.2", "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -13221,20 +16594,23 @@ } }, "node_modules/npm/node_modules/glob": { - "version": "10.4.5", + "version": "11.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -13245,14 +16621,14 @@ "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "8.1.0", + "version": "9.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "lru-cache": "^10.0.1" + "lru-cache": "^11.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/http-cache-semantics": { @@ -13297,14 +16673,14 @@ } }, "node_modules/npm/node_modules/ignore-walk": { - "version": "7.0.0", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "minimatch": "^9.0.0" + "minimatch": "^10.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/imurmurhash": { @@ -13324,30 +16700,26 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "8.2.1", + "version": "8.2.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/package-json": "^6.1.0", - "npm-package-arg": "^12.0.0", + "@npmcli/package-json": "^7.0.0", + "npm-package-arg": "^13.0.0", "promzard": "^2.0.0", "read": "^4.0.0", - "semver": "^7.3.5", + "semver": "^7.7.2", "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^6.0.0" + "validate-npm-package-name": "^6.0.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/ip-address": { - "version": "9.0.5", + "version": "10.0.1", "inBundle": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -13364,14 +16736,14 @@ } }, "node_modules/npm/node_modules/is-cidr": { - "version": "5.1.1", + "version": "6.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^4.1.1" + "cidr-regex": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { @@ -13383,29 +16755,27 @@ } }, "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", + "version": "3.1.1", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=16" + } }, "node_modules/npm/node_modules/jackspeak": { - "version": "3.4.3", + "version": "4.1.1", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/npm/node_modules/jsbn": { - "version": "1.1.0", - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "4.0.0", "inBundle": true, @@ -13441,50 +16811,51 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "10.0.1", + "version": "10.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.7", + "version": "8.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", + "@npmcli/arborist": "^9.1.5", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", - "diff": "^7.0.0", - "minimatch": "^9.0.4", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0", - "tar": "^6.2.1" + "diff": "^8.0.2", + "minimatch": "^10.0.3", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", + "tar": "^7.5.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.6", + "version": "10.1.7", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", - "@npmcli/package-json": "^6.1.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/arborist": "^9.1.5", + "@npmcli/package-json": "^7.0.0", + "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", "read": "^4.0.0", - "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", + "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "engines": { @@ -13492,54 +16863,54 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.7", + "version": "7.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4" + "@npmcli/arborist": "^9.1.5" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "8.0.0", + "version": "8.0.1", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.7", + "version": "9.0.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.4", - "@npmcli/run-script": "^9.0.1", - "npm-package-arg": "^12.0.0", - "pacote": "^21.0.0" + "@npmcli/arborist": "^9.1.5", + "@npmcli/run-script": "^10.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.1.0", + "version": "11.1.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/package-json": "^6.2.0", + "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1", + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0", "semver": "^7.3.7", - "sigstore": "^3.0.0", + "sigstore": "^4.0.0", "ssri": "^12.0.0" }, "engines": { @@ -13547,35 +16918,35 @@ } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "9.0.0", + "version": "9.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" + "npm-registry-fetch": "^19.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/git": "^7.0.0", + "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^4.0.0", "proc-log": "^5.0.0", "semver": "^7.3.7" @@ -13585,17 +16956,20 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "10.4.3", + "version": "11.2.1", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "14.0.3", + "version": "15.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", @@ -13607,26 +16981,18 @@ "ssri": "^12.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/minimatch": { - "version": "9.0.5", + "version": "10.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -13667,17 +17033,6 @@ "encoding": "^0.1.13" } }, - "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", "inBundle": true, @@ -13745,37 +17100,14 @@ } }, "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", + "version": "3.1.0", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/npm/node_modules/ms": { @@ -13791,8 +17123,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/npm/node_modules/node-gyp": { - "version": "11.2.0", + "version": "11.4.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -13814,61 +17154,129 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { "version": "3.0.0", "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "minipass": "^7.1.2" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": ">= 18" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "19.0.1", "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", + "node_modules/npm/node_modules/node-gyp/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "14.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" }, "engines": { - "node": ">=18" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { - "version": "5.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/path-scurry": { + "version": "1.11.1", "inBundle": true, "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/nopt": { @@ -13886,16 +17294,16 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "7.0.1", + "version": "8.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-audit-report": { @@ -13918,7 +17326,7 @@ } }, "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.1", + "version": "7.1.2", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -13937,83 +17345,72 @@ } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "12.0.2", + "version": "13.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^8.0.0", + "hosted-git-info": "^9.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.0", + "version": "10.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "ignore-walk": "^7.0.0" + "ignore-walk": "^8.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "10.0.0", + "version": "11.0.1", "inBundle": true, "license": "ISC", "dependencies": { "npm-install-checks": "^7.1.0", "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", + "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-profile": { - "version": "11.0.1", + "version": "12.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^18.0.0", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "18.0.2", + "version": "19.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/redact": "^3.0.0", "jsonparse": "^1.3.1", - "make-fetch-happen": "^14.0.0", + "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minizlib": "^3.0.1", - "npm-package-arg": "^12.0.0", + "npm-package-arg": "^13.0.0", "proc-log": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { - "version": "3.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/npm-user-validate": { @@ -14041,27 +17438,27 @@ "license": "BlueOak-1.0.0" }, "node_modules/npm/node_modules/pacote": { - "version": "21.0.0", + "version": "21.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", + "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", + "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^10.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", + "sigstore": "^4.0.0", "ssri": "^12.0.0", - "tar": "^6.1.11" + "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" @@ -14092,15 +17489,15 @@ } }, "node_modules/npm/node_modules/path-scurry": { - "version": "1.11.1", + "version": "2.0.0", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -14199,18 +17596,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", "inBundle": true, @@ -14267,19 +17652,19 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "@sigstore/sign": "^3.1.0", - "@sigstore/tuf": "^3.1.0", - "@sigstore/verify": "^2.1.0" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.0.0", + "@sigstore/tuf": "^4.0.0", + "@sigstore/verify": "^3.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/smart-buffer": { @@ -14292,11 +17677,11 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.8.6", + "version": "2.8.7", "inBundle": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -14350,15 +17735,10 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.21", + "version": "3.0.22", "inBundle": true, "license": "CC0-1.0" }, - "node_modules/npm/node_modules/sprintf-js": { - "version": "1.1.3", - "inBundle": true, - "license": "BSD-3-Clause" - }, "node_modules/npm/node_modules/ssri": { "version": "12.0.0", "inBundle": true, @@ -14421,7 +17801,7 @@ } }, "node_modules/npm/node_modules/supports-color": { - "version": "10.0.0", + "version": "10.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -14432,49 +17812,26 @@ } }, "node_modules/npm/node_modules/tar": { - "version": "6.2.1", + "version": "7.5.1", "inBundle": true, "license": "ISC", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minipass": { + "node_modules/npm/node_modules/tar/node_modules/yallist": { "version": "5.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/npm/node_modules/text-table": { @@ -14483,17 +17840,17 @@ "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", + "version": "2.0.2", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.14", + "version": "0.2.15", "inBundle": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -14503,9 +17860,12 @@ } }, "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", + "version": "6.5.0", "inBundle": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -14535,16 +17895,16 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "3.1.0", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { - "@tufjs/models": "3.0.1", + "@tufjs/models": "4.0.0", "debug": "^4.4.1", - "make-fetch-happen": "^14.0.3" + "make-fetch-happen": "^15.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/unique-filename": { @@ -14622,14 +17982,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/which/node_modules/isexe": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", "inBundle": true, @@ -14678,7 +18030,7 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -14710,7 +18062,7 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.1.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -14740,6 +18092,19 @@ "inBundle": true, "license": "ISC" }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nypm": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", @@ -14976,95 +18341,50 @@ } }, "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/chalk": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", - "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "node_modules/ora/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/oslo": { @@ -15487,13 +18807,23 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "license": "MIT", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "license": "ISC", "engines": { - "node": ">=16" + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/path-type": { @@ -15629,10 +18959,30 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", - "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", "dev": true, "license": "MIT", "dependencies": { @@ -15692,6 +19042,97 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -15732,12 +19173,12 @@ } }, "node_modules/posthog-node": { - "version": "5.8.4", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.8.4.tgz", - "integrity": "sha512-O0lObQqeIiggNCjc5BQx5PaHqPzXxwKnCJdb+DuNkbDO6Vc442SQ5FDv0WjPd5Ejfwme1uGZmM5/xhHWKegFfQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.2.tgz", + "integrity": "sha512-oU7FbFcH5cn40nhP04cBeT67zE76EiGWjKKzDvm6IOm5P83sqM0Ij0wMJQSHp+QI6ZN7MLzb+4xfMPUEZ4q6CA==", "license": "MIT", "dependencies": { - "@posthog/core": "1.0.2" + "@posthog/core": "1.2.2" }, "engines": { "node": ">=20" @@ -15793,6 +19234,33 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prism-react-renderer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", + "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -15937,6 +19405,16 @@ ], "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -15947,18 +19425,34 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.6.3", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/rc": { @@ -16060,6 +19554,35 @@ "node": ">=18.0.0" } }, + "node_modules/react-email/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-email/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-email/node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -16070,6 +19593,39 @@ "node": ">=18" } }, + "node_modules/react-email/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-email/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-email/node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -16080,17 +19636,139 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/react-email/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/react-email/node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", "dev": true, "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-email/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/react-email/node_modules/tsconfig-paths": { @@ -16223,6 +19901,16 @@ } } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -16265,6 +19953,27 @@ "node": ">=0.8.8" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -16313,6 +20022,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/reodotdev": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reodotdev/-/reodotdev-1.0.0.tgz", + "integrity": "sha512-wXe1vJucZjrhQL0SxOL9EvmJrtbMCIEGMdZX5lj/57n2T3UhBHZsAcM5TQASJ0T6ZBbrETRnMhH33bsbJeRO6Q==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resend": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.1.1.tgz", + "integrity": "sha512-qHip8WF4uB2k83vG5EfLWQo27anlHpQagljWLFSIXgbkmNYzoIoAsXctl/RZJl7tf+7uCLM/MEwPzyW5zwCJTA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@react-email/render": "^1.1.0" + }, + "peerDependenciesMeta": { + "@react-email/render": { + "optional": true + } + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -16352,49 +20094,17 @@ } }, "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, "node_modules/reusify": { @@ -16539,6 +20249,63 @@ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, + "node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/selderee": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", @@ -16585,6 +20352,16 @@ "node": ">= 18" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", @@ -16653,16 +20430,16 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", + "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "optional": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" + "detect-libc": "^2.0.3", + "semver": "^7.7.1" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -16671,28 +20448,26 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" + "@img/sharp-darwin-arm64": "0.34.1", + "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.1", + "@img/sharp-linux-arm64": "0.34.1", + "@img/sharp-linux-s390x": "0.34.1", + "@img/sharp-linux-x64": "0.34.1", + "@img/sharp-linuxmusl-arm64": "0.34.1", + "@img/sharp-linuxmusl-x64": "0.34.1", + "@img/sharp-wasm32": "0.34.1", + "@img/sharp-win32-ia32": "0.34.1", + "@img/sharp-win32-x64": "0.34.1" } }, "node_modules/shebang-command": { @@ -16841,9 +20616,10 @@ } }, "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" @@ -16936,6 +20712,40 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -17033,6 +20843,17 @@ "node": ">= 0.6" } }, + "node_modules/sonner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz", + "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -17063,6 +20884,12 @@ "source-map": "^0.6.0" } }, + "node_modules/spamc": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/spamc/-/spamc-0.0.5.tgz", + "integrity": "sha512-jYXItuZuiWZyG9fIdvgTUbp2MNRuyhuSwvvhhpPJd4JK/9oSZxkD7zAj53GJtowSlXwCJzLg6sCKAoE9wXsKgg==", + "dev": true + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -17087,6 +20914,25 @@ "node": "*" } }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -17298,9 +21144,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -17365,11 +21211,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stripe": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.2.1.tgz", + "integrity": "sha512-GwB1B7WSwEBzW4dilgyJruUYhbGMscrwuyHsPUmSRKrGHZ5poSh2oU9XKdii5BFVJzXHn35geRvGJ6R8bYcp8w==", + "license": "MIT", + "dependencies": { + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + }, + "peerDependencies": { + "@types/node": ">=12.x.x" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "dev": true, + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", "funding": [ { "type": "github", @@ -17401,6 +21266,126 @@ } } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/sucrase/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -17426,9 +21411,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.27.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.27.1.tgz", - "integrity": "sha512-oGtpYO3lnoaqyGtlJalvryl7TwzgRuxpOVWqEHx8af0YXI+Kt+4jMpLdgMtMcmWmuQ0QTCHLKExwrBFMSxvAUA==", + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.1.tgz", + "integrity": "sha512-qyjpz0qgcomRr41a5Aye42o69TKwCeHM9F8htLGVeUMKekNS6qAqz9oS7CtSvgGJSppSNAYAIh7vrfrSdHj9zw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -17480,17 +21465,15 @@ } }, "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -17531,12 +21514,114 @@ "node": ">=6" } }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/terser": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-lru": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.3.4.tgz", + "integrity": "sha512-UxWEfRKpFCabAf6fkTNdlfSw/RDUJ/4C6i1aLZaDnGF82PERHyYhz5CMCVYXtLt34LbqgfpJ2bjmgGKgxuF/6A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -17545,13 +21630,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -17602,6 +21687,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tsc-alias": { "version": "1.8.16", "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", @@ -17710,6 +21802,18 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -17776,9 +21880,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.8.tgz", - "integrity": "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -17796,6 +21900,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -17898,16 +22012,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", - "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.44.1", - "@typescript-eslint/parser": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1" + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -17989,6 +22103,37 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -18019,6 +22164,19 @@ } } }, + "node_modules/use-debounce": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.4.tgz", + "integrity": "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/use-intl": { "version": "4.3.9", "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.9.tgz", @@ -18105,6 +22263,30 @@ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -18114,11 +22296,116 @@ "node": ">= 8" } }, + "node_modules/webpack": { + "version": "5.102.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.0.tgz", + "integrity": "sha512-hUtqAR3ZLVEYDEABdBioQCIqSoguHbFn1K7WlPPWSuXmx0031BD73PSE35jKyftdSh4YLDoQNgK4pqBt5Q82MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.2.3", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -18364,9 +22651,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { "node": ">=12" @@ -18402,6 +22689,15 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -18421,14 +22717,11 @@ } }, "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "license": "ISC" }, "node_modules/yaml": { "version": "2.8.1", @@ -18469,9 +22762,9 @@ } }, "node_modules/yargs/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { @@ -18504,9 +22797,9 @@ } }, "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 003974c8..3b82ab35 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,21 @@ "db:sqlite:studio": "drizzle-kit studio --config=./drizzle.sqlite.config.ts", "db:pg:studio": "drizzle-kit studio --config=./drizzle.pg.config.ts", "db:clear-migrations": "rm -rf server/migrations", + "set:oss": "echo 'export const build = \"oss\" as any;' > server/build.ts", + "set:saas": "echo 'export const build = \"saas\" as any;' > server/build.ts", + "set:enterprise": "echo 'export const build = \"enterprise\" as any;' > server/build.ts", + "set:sqlite": "echo 'export * from \"./sqlite\";' > server/db/index.ts", + "set:pg": "echo 'export * from \"./pg\";' > server/db/index.ts", "build:sqlite": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs", "build:pg": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs", "start": "ENVIRONMENT=prod node dist/migrations.mjs && ENVIRONMENT=prod NODE_ENV=development node --enable-source-maps dist/server.mjs", "email": "email dev --dir server/emails/templates --port 3005", - "build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs" + "build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs", + "db:sqlite:seed-exit-node": "sqlite3 config/db/db.sqlite \"INSERT INTO exitNodes (exitNodeId, name, address, endpoint, publicKey, listenPort, reachableAt, maxConnections, online, lastPing, type, region) VALUES (null, 'test', '10.0.0.1/24', 'localhost', 'MJ44MpnWGxMZURgxW/fWXDFsejhabnEFYDo60LQwK3A=', 1234, 'http://localhost:3003', 123, 1, null, 'gerbil', null);\"" }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", + "@aws-sdk/client-s3": "3.837.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -78,10 +85,12 @@ "http-errors": "2.0.0", "i": "^0.3.7", "input-otp": "1.4.2", + "ioredis": "5.6.1", "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.544.0", + "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.3", "next-intl": "^4.3.9", @@ -100,7 +109,10 @@ "react-hook-form": "7.62.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", + "reodotdev": "^1.0.0", + "resend": "^6.1.1", "semver": "^7.7.2", + "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", "tailwind-merge": "3.3.1", "tw-animate-css": "^1.3.8", @@ -116,6 +128,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", + "@react-email/preview-server": "4.1.0", "@tailwindcss/postcss": "^4.1.13", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", diff --git a/server/apiServer.ts b/server/apiServer.ts index a400555b..7c64c14f 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -7,16 +7,20 @@ import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares"; +import { corsWithLoginPageSupport } from "@server/middlewares/private/corsWithLoginPage"; import { authenticated, unauthenticated } from "@server/routers/external"; import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; import { csrfProtectionMiddleware } from "./middlewares/csrfProtection"; import helmet from "helmet"; +import { stripeWebhookHandler } from "@server/routers/private/billing/webhooks"; +import { build } from "./build"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import HttpCode from "./types/HttpCode"; import requestTimeoutMiddleware from "./middlewares/requestTimeout"; -import { createStore } from "./lib/rateLimitStore"; +import { createStore } from "@server/lib/private/rateLimitStore"; +import hybridRouter from "@server/routers/private/hybrid"; const dev = config.isDev; const externalPort = config.getRawConfig().server.external_port; @@ -30,26 +34,39 @@ export function createApiServer() { apiServer.set("trust proxy", trustProxy); } + if (build == "saas") { + apiServer.post( + `${prefix}/billing/webhooks`, + express.raw({ type: "application/json" }), + stripeWebhookHandler + ); + } + const corsConfig = config.getRawConfig().server.cors; - const options = { - ...(corsConfig?.origins - ? { origin: corsConfig.origins } - : { - origin: (origin: any, callback: any) => { - callback(null, true); - } - }), - ...(corsConfig?.methods && { methods: corsConfig.methods }), - ...(corsConfig?.allowed_headers && { - allowedHeaders: corsConfig.allowed_headers - }), - credentials: !(corsConfig?.credentials === false) - }; + if (build == "oss") { + const options = { + ...(corsConfig?.origins + ? { origin: corsConfig.origins } + : { + origin: (origin: any, callback: any) => { + callback(null, true); + } + }), + ...(corsConfig?.methods && { methods: corsConfig.methods }), + ...(corsConfig?.allowed_headers && { + allowedHeaders: corsConfig.allowed_headers + }), + credentials: !(corsConfig?.credentials === false) + }; - logger.debug("Using CORS options", options); + logger.debug("Using CORS options", options); - apiServer.use(cors(options)); + apiServer.use(cors(options)); + } else { + // Use the custom CORS middleware with loginPage support + apiServer.use(corsWithLoginPageSupport(corsConfig)); + } if (!dev) { apiServer.use(helmet()); @@ -70,7 +87,8 @@ export function createApiServer() { 60 * 1000, max: config.getRawConfig().rate_limits.global.max_requests, - keyGenerator: (req) => `apiServerGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`, + keyGenerator: (req) => + `apiServerGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`, handler: (req, res, next) => { const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`; return next( @@ -85,6 +103,9 @@ export function createApiServer() { // API routes apiServer.use(logIncomingMiddleware); apiServer.use(prefix, unauthenticated); + if (build !== "oss") { + apiServer.use(`${prefix}/hybrid`, hybridRouter); + } apiServer.use(prefix, authenticated); // WebSocket routes diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 6b6c9bf4..45d53eaa 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -4,6 +4,7 @@ import { userActions, roleActions, userOrgs } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { sendUsageNotification } from "@server/routers/org"; export enum ActionsEnum { createOrgUser = "createOrgUser", @@ -98,10 +99,23 @@ export enum ActionsEnum { listApiKeyActions = "listApiKeyActions", listApiKeys = "listApiKeys", getApiKey = "getApiKey", + getCertificate = "getCertificate", + restartCertificate = "restartCertificate", + billing = "billing", createOrgDomain = "createOrgDomain", deleteOrgDomain = "deleteOrgDomain", restartOrgDomain = "restartOrgDomain", + sendUsageNotification = "sendUsageNotification", + createRemoteExitNode = "createRemoteExitNode", + updateRemoteExitNode = "updateRemoteExitNode", + getRemoteExitNode = "getRemoteExitNode", + listRemoteExitNode = "listRemoteExitNode", + deleteRemoteExitNode = "deleteRemoteExitNode", updateOrgUser = "updateOrgUser", + createLoginPage = "createLoginPage", + updateLoginPage = "updateLoginPage", + getLoginPage = "getLoginPage", + deleteLoginPage = "deleteLoginPage", applyBlueprint = "applyBlueprint" } diff --git a/server/auth/sessions/app.ts b/server/auth/sessions/app.ts index 514bee00..e846396d 100644 --- a/server/auth/sessions/app.ts +++ b/server/auth/sessions/app.ts @@ -3,13 +3,7 @@ import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; -import { - resourceSessions, - Session, - sessions, - User, - users -} from "@server/db"; +import { resourceSessions, Session, sessions, User, users } from "@server/db"; import { db } from "@server/db"; import { eq, inArray } from "drizzle-orm"; import config from "@server/lib/config"; @@ -24,8 +18,9 @@ export const SESSION_COOKIE_EXPIRES = 60 * 60 * config.getRawConfig().server.dashboard_session_length_hours; -export const COOKIE_DOMAIN = config.getRawConfig().app.dashboard_url ? - "." + new URL(config.getRawConfig().app.dashboard_url!).hostname : undefined; +export const COOKIE_DOMAIN = config.getRawConfig().app.dashboard_url + ? new URL(config.getRawConfig().app.dashboard_url!).hostname + : undefined; export function generateSessionToken(): string { const bytes = new Uint8Array(20); @@ -98,8 +93,8 @@ export async function invalidateSession(sessionId: string): Promise { try { await db.transaction(async (trx) => { await trx - .delete(resourceSessions) - .where(eq(resourceSessions.userSessionId, sessionId)); + .delete(resourceSessions) + .where(eq(resourceSessions.userSessionId, sessionId)); await trx.delete(sessions).where(eq(sessions.sessionId, sessionId)); }); } catch (e) { @@ -111,9 +106,9 @@ export async function invalidateAllSessions(userId: string): Promise { try { await db.transaction(async (trx) => { const userSessions = await trx - .select() - .from(sessions) - .where(eq(sessions.userId, userId)); + .select() + .from(sessions) + .where(eq(sessions.userId, userId)); await trx.delete(resourceSessions).where( inArray( resourceSessions.userSessionId, diff --git a/server/auth/sessions/privateRemoteExitNode.ts b/server/auth/sessions/privateRemoteExitNode.ts new file mode 100644 index 00000000..fbb2ae1f --- /dev/null +++ b/server/auth/sessions/privateRemoteExitNode.ts @@ -0,0 +1,85 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + encodeHexLowerCase, +} from "@oslojs/encoding"; +import { sha256 } from "@oslojs/crypto/sha2"; +import { RemoteExitNode, remoteExitNodes, remoteExitNodeSessions, RemoteExitNodeSession } from "@server/db"; +import { db } from "@server/db"; +import { eq } from "drizzle-orm"; + +export const EXPIRES = 1000 * 60 * 60 * 24 * 30; + +export async function createRemoteExitNodeSession( + token: string, + remoteExitNodeId: string, +): Promise { + const sessionId = encodeHexLowerCase( + sha256(new TextEncoder().encode(token)), + ); + const session: RemoteExitNodeSession = { + sessionId: sessionId, + remoteExitNodeId, + expiresAt: new Date(Date.now() + EXPIRES).getTime(), + }; + await db.insert(remoteExitNodeSessions).values(session); + return session; +} + +export async function validateRemoteExitNodeSessionToken( + token: string, +): Promise { + const sessionId = encodeHexLowerCase( + sha256(new TextEncoder().encode(token)), + ); + const result = await db + .select({ remoteExitNode: remoteExitNodes, session: remoteExitNodeSessions }) + .from(remoteExitNodeSessions) + .innerJoin(remoteExitNodes, eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodes.remoteExitNodeId)) + .where(eq(remoteExitNodeSessions.sessionId, sessionId)); + if (result.length < 1) { + return { session: null, remoteExitNode: null }; + } + const { remoteExitNode, session } = result[0]; + if (Date.now() >= session.expiresAt) { + await db + .delete(remoteExitNodeSessions) + .where(eq(remoteExitNodeSessions.sessionId, session.sessionId)); + return { session: null, remoteExitNode: null }; + } + if (Date.now() >= session.expiresAt - (EXPIRES / 2)) { + session.expiresAt = new Date( + Date.now() + EXPIRES, + ).getTime(); + await db + .update(remoteExitNodeSessions) + .set({ + expiresAt: session.expiresAt, + }) + .where(eq(remoteExitNodeSessions.sessionId, session.sessionId)); + } + return { session, remoteExitNode }; +} + +export async function invalidateRemoteExitNodeSession(sessionId: string): Promise { + await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.sessionId, sessionId)); +} + +export async function invalidateAllRemoteExitNodeSessions(remoteExitNodeId: string): Promise { + await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodeId)); +} + +export type SessionValidationResult = + | { session: RemoteExitNodeSession; remoteExitNode: RemoteExitNode } + | { session: null; remoteExitNode: null }; diff --git a/server/auth/sessions/resource.ts b/server/auth/sessions/resource.ts index 511dadda..a378202e 100644 --- a/server/auth/sessions/resource.ts +++ b/server/auth/sessions/resource.ts @@ -199,14 +199,14 @@ export function serializeResourceSessionCookie( const now = new Date().getTime(); if (!isHttp) { if (expiresAt === undefined) { - return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${"." + domain}`; + return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${domain}`; } - return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${"." + domain}`; + return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${domain}`; } else { if (expiresAt === undefined) { - return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=${"." + domain}`; + return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=$domain}`; } - return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${"." + domain}`; + return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${domain}`; } } @@ -216,9 +216,9 @@ export function createBlankResourceSessionTokenCookie( isHttp: boolean = false ): string { if (!isHttp) { - return `${cookieName}_s=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure; Domain=${"." + domain}`; + return `${cookieName}_s=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure; Domain=${domain}`; } else { - return `${cookieName}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Domain=${"." + domain}`; + return `${cookieName}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Domain=${domain}`; } } diff --git a/server/build.ts b/server/build.ts deleted file mode 100644 index babe5e8b..00000000 --- a/server/build.ts +++ /dev/null @@ -1 +0,0 @@ -export const build = "oss" as any; diff --git a/server/db/countries.ts b/server/db/countries.ts new file mode 100644 index 00000000..52eb2b4b --- /dev/null +++ b/server/db/countries.ts @@ -0,0 +1,1014 @@ +export const COUNTRIES = [ + { + "name": "ALL COUNTRIES", + "code": "ALL" // THIS IS AN INVALID CC SO IT WILL NEVER MATCH + }, + { + "name": "Afghanistan", + "code": "AF" + }, + { + "name": "Albania", + "code": "AL" + }, + { + "name": "Algeria", + "code": "DZ" + }, + { + "name": "American Samoa", + "code": "AS" + }, + { + "name": "Andorra", + "code": "AD" + }, + { + "name": "Angola", + "code": "AO" + }, + { + "name": "Anguilla", + "code": "AI" + }, + { + "name": "Antarctica", + "code": "AQ" + }, + { + "name": "Antigua and Barbuda", + "code": "AG" + }, + { + "name": "Argentina", + "code": "AR" + }, + { + "name": "Armenia", + "code": "AM" + }, + { + "name": "Aruba", + "code": "AW" + }, + { + "name": "Asia/Pacific Region", + "code": "AP" + }, + { + "name": "Australia", + "code": "AU" + }, + { + "name": "Austria", + "code": "AT" + }, + { + "name": "Azerbaijan", + "code": "AZ" + }, + { + "name": "Bahamas", + "code": "BS" + }, + { + "name": "Bahrain", + "code": "BH" + }, + { + "name": "Bangladesh", + "code": "BD" + }, + { + "name": "Barbados", + "code": "BB" + }, + { + "name": "Belarus", + "code": "BY" + }, + { + "name": "Belgium", + "code": "BE" + }, + { + "name": "Belize", + "code": "BZ" + }, + { + "name": "Benin", + "code": "BJ" + }, + { + "name": "Bermuda", + "code": "BM" + }, + { + "name": "Bhutan", + "code": "BT" + }, + { + "name": "Bolivia", + "code": "BO" + }, + { + "name": "Bonaire, Sint Eustatius and Saba", + "code": "BQ" + }, + { + "name": "Bosnia and Herzegovina", + "code": "BA" + }, + { + "name": "Botswana", + "code": "BW" + }, + { + "name": "Bouvet Island", + "code": "BV" + }, + { + "name": "Brazil", + "code": "BR" + }, + { + "name": "British Indian Ocean Territory", + "code": "IO" + }, + { + "name": "Brunei Darussalam", + "code": "BN" + }, + { + "name": "Bulgaria", + "code": "BG" + }, + { + "name": "Burkina Faso", + "code": "BF" + }, + { + "name": "Burundi", + "code": "BI" + }, + { + "name": "Cambodia", + "code": "KH" + }, + { + "name": "Cameroon", + "code": "CM" + }, + { + "name": "Canada", + "code": "CA" + }, + { + "name": "Cape Verde", + "code": "CV" + }, + { + "name": "Cayman Islands", + "code": "KY" + }, + { + "name": "Central African Republic", + "code": "CF" + }, + { + "name": "Chad", + "code": "TD" + }, + { + "name": "Chile", + "code": "CL" + }, + { + "name": "China", + "code": "CN" + }, + { + "name": "Christmas Island", + "code": "CX" + }, + { + "name": "Cocos (Keeling) Islands", + "code": "CC" + }, + { + "name": "Colombia", + "code": "CO" + }, + { + "name": "Comoros", + "code": "KM" + }, + { + "name": "Congo", + "code": "CG" + }, + { + "name": "Congo, The Democratic Republic of the", + "code": "CD" + }, + { + "name": "Cook Islands", + "code": "CK" + }, + { + "name": "Costa Rica", + "code": "CR" + }, + { + "name": "Croatia", + "code": "HR" + }, + { + "name": "Cuba", + "code": "CU" + }, + { + "name": "Curaçao", + "code": "CW" + }, + { + "name": "Cyprus", + "code": "CY" + }, + { + "name": "Czech Republic", + "code": "CZ" + }, + { + "name": "Côte d'Ivoire", + "code": "CI" + }, + { + "name": "Denmark", + "code": "DK" + }, + { + "name": "Djibouti", + "code": "DJ" + }, + { + "name": "Dominica", + "code": "DM" + }, + { + "name": "Dominican Republic", + "code": "DO" + }, + { + "name": "Ecuador", + "code": "EC" + }, + { + "name": "Egypt", + "code": "EG" + }, + { + "name": "El Salvador", + "code": "SV" + }, + { + "name": "Equatorial Guinea", + "code": "GQ" + }, + { + "name": "Eritrea", + "code": "ER" + }, + { + "name": "Estonia", + "code": "EE" + }, + { + "name": "Ethiopia", + "code": "ET" + }, + { + "name": "Falkland Islands (Malvinas)", + "code": "FK" + }, + { + "name": "Faroe Islands", + "code": "FO" + }, + { + "name": "Fiji", + "code": "FJ" + }, + { + "name": "Finland", + "code": "FI" + }, + { + "name": "France", + "code": "FR" + }, + { + "name": "French Guiana", + "code": "GF" + }, + { + "name": "French Polynesia", + "code": "PF" + }, + { + "name": "French Southern Territories", + "code": "TF" + }, + { + "name": "Gabon", + "code": "GA" + }, + { + "name": "Gambia", + "code": "GM" + }, + { + "name": "Georgia", + "code": "GE" + }, + { + "name": "Germany", + "code": "DE" + }, + { + "name": "Ghana", + "code": "GH" + }, + { + "name": "Gibraltar", + "code": "GI" + }, + { + "name": "Greece", + "code": "GR" + }, + { + "name": "Greenland", + "code": "GL" + }, + { + "name": "Grenada", + "code": "GD" + }, + { + "name": "Guadeloupe", + "code": "GP" + }, + { + "name": "Guam", + "code": "GU" + }, + { + "name": "Guatemala", + "code": "GT" + }, + { + "name": "Guernsey", + "code": "GG" + }, + { + "name": "Guinea", + "code": "GN" + }, + { + "name": "Guinea-Bissau", + "code": "GW" + }, + { + "name": "Guyana", + "code": "GY" + }, + { + "name": "Haiti", + "code": "HT" + }, + { + "name": "Heard Island and Mcdonald Islands", + "code": "HM" + }, + { + "name": "Holy See (Vatican City State)", + "code": "VA" + }, + { + "name": "Honduras", + "code": "HN" + }, + { + "name": "Hong Kong", + "code": "HK" + }, + { + "name": "Hungary", + "code": "HU" + }, + { + "name": "Iceland", + "code": "IS" + }, + { + "name": "India", + "code": "IN" + }, + { + "name": "Indonesia", + "code": "ID" + }, + { + "name": "Iran, Islamic Republic Of", + "code": "IR" + }, + { + "name": "Iraq", + "code": "IQ" + }, + { + "name": "Ireland", + "code": "IE" + }, + { + "name": "Isle of Man", + "code": "IM" + }, + { + "name": "Israel", + "code": "IL" + }, + { + "name": "Italy", + "code": "IT" + }, + { + "name": "Jamaica", + "code": "JM" + }, + { + "name": "Japan", + "code": "JP" + }, + { + "name": "Jersey", + "code": "JE" + }, + { + "name": "Jordan", + "code": "JO" + }, + { + "name": "Kazakhstan", + "code": "KZ" + }, + { + "name": "Kenya", + "code": "KE" + }, + { + "name": "Kiribati", + "code": "KI" + }, + { + "name": "Korea, Republic of", + "code": "KR" + }, + { + "name": "Kuwait", + "code": "KW" + }, + { + "name": "Kyrgyzstan", + "code": "KG" + }, + { + "name": "Laos", + "code": "LA" + }, + { + "name": "Latvia", + "code": "LV" + }, + { + "name": "Lebanon", + "code": "LB" + }, + { + "name": "Lesotho", + "code": "LS" + }, + { + "name": "Liberia", + "code": "LR" + }, + { + "name": "Libyan Arab Jamahiriya", + "code": "LY" + }, + { + "name": "Liechtenstein", + "code": "LI" + }, + { + "name": "Lithuania", + "code": "LT" + }, + { + "name": "Luxembourg", + "code": "LU" + }, + { + "name": "Macao", + "code": "MO" + }, + { + "name": "Madagascar", + "code": "MG" + }, + { + "name": "Malawi", + "code": "MW" + }, + { + "name": "Malaysia", + "code": "MY" + }, + { + "name": "Maldives", + "code": "MV" + }, + { + "name": "Mali", + "code": "ML" + }, + { + "name": "Malta", + "code": "MT" + }, + { + "name": "Marshall Islands", + "code": "MH" + }, + { + "name": "Martinique", + "code": "MQ" + }, + { + "name": "Mauritania", + "code": "MR" + }, + { + "name": "Mauritius", + "code": "MU" + }, + { + "name": "Mayotte", + "code": "YT" + }, + { + "name": "Mexico", + "code": "MX" + }, + { + "name": "Micronesia, Federated States of", + "code": "FM" + }, + { + "name": "Moldova, Republic of", + "code": "MD" + }, + { + "name": "Monaco", + "code": "MC" + }, + { + "name": "Mongolia", + "code": "MN" + }, + { + "name": "Montenegro", + "code": "ME" + }, + { + "name": "Montserrat", + "code": "MS" + }, + { + "name": "Morocco", + "code": "MA" + }, + { + "name": "Mozambique", + "code": "MZ" + }, + { + "name": "Myanmar", + "code": "MM" + }, + { + "name": "Namibia", + "code": "NA" + }, + { + "name": "Nauru", + "code": "NR" + }, + { + "name": "Nepal", + "code": "NP" + }, + { + "name": "Netherlands", + "code": "NL" + }, + { + "name": "Netherlands Antilles", + "code": "AN" + }, + { + "name": "New Caledonia", + "code": "NC" + }, + { + "name": "New Zealand", + "code": "NZ" + }, + { + "name": "Nicaragua", + "code": "NI" + }, + { + "name": "Niger", + "code": "NE" + }, + { + "name": "Nigeria", + "code": "NG" + }, + { + "name": "Niue", + "code": "NU" + }, + { + "name": "Norfolk Island", + "code": "NF" + }, + { + "name": "North Korea", + "code": "KP" + }, + { + "name": "North Macedonia", + "code": "MK" + }, + { + "name": "Northern Mariana Islands", + "code": "MP" + }, + { + "name": "Norway", + "code": "NO" + }, + { + "name": "Oman", + "code": "OM" + }, + { + "name": "Pakistan", + "code": "PK" + }, + { + "name": "Palau", + "code": "PW" + }, + { + "name": "Palestinian Territory, Occupied", + "code": "PS" + }, + { + "name": "Panama", + "code": "PA" + }, + { + "name": "Papua New Guinea", + "code": "PG" + }, + { + "name": "Paraguay", + "code": "PY" + }, + { + "name": "Peru", + "code": "PE" + }, + { + "name": "Philippines", + "code": "PH" + }, + { + "name": "Pitcairn Islands", + "code": "PN" + }, + { + "name": "Poland", + "code": "PL" + }, + { + "name": "Portugal", + "code": "PT" + }, + { + "name": "Puerto Rico", + "code": "PR" + }, + { + "name": "Qatar", + "code": "QA" + }, + { + "name": "Reunion", + "code": "RE" + }, + { + "name": "Romania", + "code": "RO" + }, + { + "name": "Russian Federation", + "code": "RU" + }, + { + "name": "Rwanda", + "code": "RW" + }, + { + "name": "Saint Barthélemy", + "code": "BL" + }, + { + "name": "Saint Helena", + "code": "SH" + }, + { + "name": "Saint Kitts and Nevis", + "code": "KN" + }, + { + "name": "Saint Lucia", + "code": "LC" + }, + { + "name": "Saint Martin", + "code": "MF" + }, + { + "name": "Saint Pierre and Miquelon", + "code": "PM" + }, + { + "name": "Saint Vincent and the Grenadines", + "code": "VC" + }, + { + "name": "Samoa", + "code": "WS" + }, + { + "name": "San Marino", + "code": "SM" + }, + { + "name": "Sao Tome and Principe", + "code": "ST" + }, + { + "name": "Saudi Arabia", + "code": "SA" + }, + { + "name": "Senegal", + "code": "SN" + }, + { + "name": "Serbia", + "code": "RS" + }, + { + "name": "Serbia and Montenegro", + "code": "CS" + }, + { + "name": "Seychelles", + "code": "SC" + }, + { + "name": "Sierra Leone", + "code": "SL" + }, + { + "name": "Singapore", + "code": "SG" + }, + { + "name": "Sint Maarten", + "code": "SX" + }, + { + "name": "Slovakia", + "code": "SK" + }, + { + "name": "Slovenia", + "code": "SI" + }, + { + "name": "Solomon Islands", + "code": "SB" + }, + { + "name": "Somalia", + "code": "SO" + }, + { + "name": "South Africa", + "code": "ZA" + }, + { + "name": "South Georgia and the South Sandwich Islands", + "code": "GS" + }, + { + "name": "South Sudan", + "code": "SS" + }, + { + "name": "Spain", + "code": "ES" + }, + { + "name": "Sri Lanka", + "code": "LK" + }, + { + "name": "Sudan", + "code": "SD" + }, + { + "name": "Suriname", + "code": "SR" + }, + { + "name": "Svalbard and Jan Mayen", + "code": "SJ" + }, + { + "name": "Swaziland", + "code": "SZ" + }, + { + "name": "Sweden", + "code": "SE" + }, + { + "name": "Switzerland", + "code": "CH" + }, + { + "name": "Syrian Arab Republic", + "code": "SY" + }, + { + "name": "Taiwan", + "code": "TW" + }, + { + "name": "Tajikistan", + "code": "TJ" + }, + { + "name": "Tanzania, United Republic of", + "code": "TZ" + }, + { + "name": "Thailand", + "code": "TH" + }, + { + "name": "Timor-Leste", + "code": "TL" + }, + { + "name": "Togo", + "code": "TG" + }, + { + "name": "Tokelau", + "code": "TK" + }, + { + "name": "Tonga", + "code": "TO" + }, + { + "name": "Trinidad and Tobago", + "code": "TT" + }, + { + "name": "Tunisia", + "code": "TN" + }, + { + "name": "Turkey", + "code": "TR" + }, + { + "name": "Turkmenistan", + "code": "TM" + }, + { + "name": "Turks and Caicos Islands", + "code": "TC" + }, + { + "name": "Tuvalu", + "code": "TV" + }, + { + "name": "Uganda", + "code": "UG" + }, + { + "name": "Ukraine", + "code": "UA" + }, + { + "name": "United Arab Emirates", + "code": "AE" + }, + { + "name": "United Kingdom", + "code": "GB" + }, + { + "name": "United States", + "code": "US" + }, + { + "name": "United States Minor Outlying Islands", + "code": "UM" + }, + { + "name": "Uruguay", + "code": "UY" + }, + { + "name": "Uzbekistan", + "code": "UZ" + }, + { + "name": "Vanuatu", + "code": "VU" + }, + { + "name": "Venezuela", + "code": "VE" + }, + { + "name": "Vietnam", + "code": "VN" + }, + { + "name": "Virgin Islands, British", + "code": "VG" + }, + { + "name": "Virgin Islands, U.S.", + "code": "VI" + }, + { + "name": "Wallis and Futuna", + "code": "WF" + }, + { + "name": "Western Sahara", + "code": "EH" + }, + { + "name": "Yemen", + "code": "YE" + }, + { + "name": "Zambia", + "code": "ZM" + }, + { + "name": "Zimbabwe", + "code": "ZW" + }, + { + "name": "Åland Islands", + "code": "AX" + } +] \ No newline at end of file diff --git a/server/db/maxmind.ts b/server/db/maxmind.ts new file mode 100644 index 00000000..ca398df2 --- /dev/null +++ b/server/db/maxmind.ts @@ -0,0 +1,13 @@ +import maxmind, { CountryResponse, Reader } from "maxmind"; +import config from "@server/lib/config"; + +let maxmindLookup: Reader | null; +if (config.getRawConfig().server.maxmind_db_path) { + maxmindLookup = await maxmind.open( + config.getRawConfig().server.maxmind_db_path! + ); +} else { + maxmindLookup = null; +} + +export { maxmindLookup }; diff --git a/server/db/pg/driver.ts b/server/db/pg/driver.ts index 34d16aa1..44b210b0 100644 --- a/server/db/pg/driver.ts +++ b/server/db/pg/driver.ts @@ -39,7 +39,7 @@ function createDb() { connectionString, max: 20, idleTimeoutMillis: 30000, - connectionTimeoutMillis: 2000, + connectionTimeoutMillis: 5000, }); const replicas = []; @@ -52,7 +52,7 @@ function createDb() { connectionString: conn.connection_string, max: 10, idleTimeoutMillis: 30000, - connectionTimeoutMillis: 2000, + connectionTimeoutMillis: 5000, }); replicas.push(DrizzlePostgres(replicaPool)); } diff --git a/server/db/pg/index.ts b/server/db/pg/index.ts index 4829c04c..5cc80e86 100644 --- a/server/db/pg/index.ts +++ b/server/db/pg/index.ts @@ -1,2 +1,3 @@ export * from "./driver"; -export * from "./schema"; \ No newline at end of file +export * from "./schema"; +export * from "./privateSchema"; diff --git a/server/db/pg/privateSchema.ts b/server/db/pg/privateSchema.ts new file mode 100644 index 00000000..8ea8f9de --- /dev/null +++ b/server/db/pg/privateSchema.ts @@ -0,0 +1,245 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + pgTable, + serial, + varchar, + boolean, + integer, + bigint, + real, + text +} from "drizzle-orm/pg-core"; +import { InferSelectModel } from "drizzle-orm"; +import { domains, orgs, targets, users, exitNodes, sessions } from "./schema"; + +export const certificates = pgTable("certificates", { + certId: serial("certId").primaryKey(), + domain: varchar("domain", { length: 255 }).notNull().unique(), + domainId: varchar("domainId").references(() => domains.domainId, { + onDelete: "cascade" + }), + wildcard: boolean("wildcard").default(false), + status: varchar("status", { length: 50 }).notNull().default("pending"), // pending, requested, valid, expired, failed + expiresAt: bigint("expiresAt", { mode: "number" }), + lastRenewalAttempt: bigint("lastRenewalAttempt", { mode: "number" }), + createdAt: bigint("createdAt", { mode: "number" }).notNull(), + updatedAt: bigint("updatedAt", { mode: "number" }).notNull(), + orderId: varchar("orderId", { length: 500 }), + errorMessage: text("errorMessage"), + renewalCount: integer("renewalCount").default(0), + certFile: text("certFile"), + keyFile: text("keyFile") +}); + +export const dnsChallenge = pgTable("dnsChallenges", { + dnsChallengeId: serial("dnsChallengeId").primaryKey(), + domain: varchar("domain", { length: 255 }).notNull(), + token: varchar("token", { length: 255 }).notNull(), + keyAuthorization: varchar("keyAuthorization", { length: 1000 }).notNull(), + createdAt: bigint("createdAt", { mode: "number" }).notNull(), + expiresAt: bigint("expiresAt", { mode: "number" }).notNull(), + completed: boolean("completed").default(false) +}); + +export const account = pgTable("account", { + accountId: serial("accountId").primaryKey(), + userId: varchar("userId") + .notNull() + .references(() => users.userId, { onDelete: "cascade" }) +}); + +export const customers = pgTable("customers", { + customerId: varchar("customerId", { length: 255 }).primaryKey().notNull(), + orgId: varchar("orgId", { length: 255 }) + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + // accountId: integer("accountId") + // .references(() => account.accountId, { onDelete: "cascade" }), // Optional, if using accounts + email: varchar("email", { length: 255 }), + name: varchar("name", { length: 255 }), + phone: varchar("phone", { length: 50 }), + address: text("address"), + createdAt: bigint("createdAt", { mode: "number" }).notNull(), + updatedAt: bigint("updatedAt", { mode: "number" }).notNull() +}); + +export const subscriptions = pgTable("subscriptions", { + subscriptionId: varchar("subscriptionId", { length: 255 }) + .primaryKey() + .notNull(), + customerId: varchar("customerId", { length: 255 }) + .notNull() + .references(() => customers.customerId, { onDelete: "cascade" }), + status: varchar("status", { length: 50 }).notNull().default("active"), // active, past_due, canceled, unpaid + canceledAt: bigint("canceledAt", { mode: "number" }), + createdAt: bigint("createdAt", { mode: "number" }).notNull(), + updatedAt: bigint("updatedAt", { mode: "number" }), + billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" }) +}); + +export const subscriptionItems = pgTable("subscriptionItems", { + subscriptionItemId: serial("subscriptionItemId").primaryKey(), + subscriptionId: varchar("subscriptionId", { length: 255 }) + .notNull() + .references(() => subscriptions.subscriptionId, { + onDelete: "cascade" + }), + planId: varchar("planId", { length: 255 }).notNull(), + priceId: varchar("priceId", { length: 255 }), + meterId: varchar("meterId", { length: 255 }), + unitAmount: real("unitAmount"), + tiers: text("tiers"), + interval: varchar("interval", { length: 50 }), + currentPeriodStart: bigint("currentPeriodStart", { mode: "number" }), + currentPeriodEnd: bigint("currentPeriodEnd", { mode: "number" }), + name: varchar("name", { length: 255 }) +}); + +export const accountDomains = pgTable("accountDomains", { + accountId: integer("accountId") + .notNull() + .references(() => account.accountId, { onDelete: "cascade" }), + domainId: varchar("domainId") + .notNull() + .references(() => domains.domainId, { onDelete: "cascade" }) +}); + +export const usage = pgTable("usage", { + usageId: varchar("usageId", { length: 255 }).primaryKey(), + featureId: varchar("featureId", { length: 255 }).notNull(), + orgId: varchar("orgId") + .references(() => orgs.orgId, { onDelete: "cascade" }) + .notNull(), + meterId: varchar("meterId", { length: 255 }), + instantaneousValue: real("instantaneousValue"), + latestValue: real("latestValue").notNull(), + previousValue: real("previousValue"), + updatedAt: bigint("updatedAt", { mode: "number" }).notNull(), + rolledOverAt: bigint("rolledOverAt", { mode: "number" }), + nextRolloverAt: bigint("nextRolloverAt", { mode: "number" }) +}); + +export const limits = pgTable("limits", { + limitId: varchar("limitId", { length: 255 }).primaryKey(), + featureId: varchar("featureId", { length: 255 }).notNull(), + orgId: varchar("orgId") + .references(() => orgs.orgId, { + onDelete: "cascade" + }) + .notNull(), + value: real("value"), + description: text("description") +}); + +export const usageNotifications = pgTable("usageNotifications", { + notificationId: serial("notificationId").primaryKey(), + orgId: varchar("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + featureId: varchar("featureId", { length: 255 }).notNull(), + limitId: varchar("limitId", { length: 255 }).notNull(), + notificationType: varchar("notificationType", { length: 50 }).notNull(), + sentAt: bigint("sentAt", { mode: "number" }).notNull() +}); + +export const domainNamespaces = pgTable("domainNamespaces", { + domainNamespaceId: varchar("domainNamespaceId", { + length: 255 + }).primaryKey(), + domainId: varchar("domainId") + .references(() => domains.domainId, { + onDelete: "set null" + }) + .notNull() +}); + +export const exitNodeOrgs = pgTable("exitNodeOrgs", { + exitNodeId: integer("exitNodeId") + .notNull() + .references(() => exitNodes.exitNodeId, { onDelete: "cascade" }), + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }) +}); + +export const remoteExitNodes = pgTable("remoteExitNode", { + remoteExitNodeId: varchar("id").primaryKey(), + secretHash: varchar("secretHash").notNull(), + dateCreated: varchar("dateCreated").notNull(), + version: varchar("version"), + exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { + onDelete: "cascade" + }) +}); + +export const remoteExitNodeSessions = pgTable("remoteExitNodeSession", { + sessionId: varchar("id").primaryKey(), + remoteExitNodeId: varchar("remoteExitNodeId") + .notNull() + .references(() => remoteExitNodes.remoteExitNodeId, { + onDelete: "cascade" + }), + expiresAt: bigint("expiresAt", { mode: "number" }).notNull() +}); + +export const loginPage = pgTable("loginPage", { + loginPageId: serial("loginPageId").primaryKey(), + subdomain: varchar("subdomain"), + fullDomain: varchar("fullDomain"), + exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { + onDelete: "set null" + }), + domainId: varchar("domainId").references(() => domains.domainId, { + onDelete: "set null" + }) +}); + +export const loginPageOrg = pgTable("loginPageOrg", { + loginPageId: integer("loginPageId") + .notNull() + .references(() => loginPage.loginPageId, { onDelete: "cascade" }), + orgId: varchar("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }) +}); + +export const sessionTransferToken = pgTable("sessionTransferToken", { + token: varchar("token").primaryKey(), + sessionId: varchar("sessionId") + .notNull() + .references(() => sessions.sessionId, { + onDelete: "cascade" + }), + encryptedSession: text("encryptedSession").notNull(), + expiresAt: bigint("expiresAt", { mode: "number" }).notNull() +}); + +export type Limit = InferSelectModel; +export type Account = InferSelectModel; +export type Certificate = InferSelectModel; +export type DnsChallenge = InferSelectModel; +export type Customer = InferSelectModel; +export type Subscription = InferSelectModel; +export type SubscriptionItem = InferSelectModel; +export type Usage = InferSelectModel; +export type UsageLimit = InferSelectModel; +export type AccountDomain = InferSelectModel; +export type UsageNotification = InferSelectModel; +export type RemoteExitNode = InferSelectModel; +export type RemoteExitNodeSession = InferSelectModel< + typeof remoteExitNodeSessions +>; +export type ExitNodeOrg = InferSelectModel; +export type LoginPage = InferSelectModel; diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 18b29f35..29c14560 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -128,6 +128,27 @@ export const targets = pgTable("targets", { rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix }); +export const targetHealthCheck = pgTable("targetHealthCheck", { + targetHealthCheckId: serial("targetHealthCheckId").primaryKey(), + targetId: integer("targetId") + .notNull() + .references(() => targets.targetId, { onDelete: "cascade" }), + hcEnabled: boolean("hcEnabled").notNull().default(false), + hcPath: varchar("hcPath"), + hcScheme: varchar("hcScheme"), + hcMode: varchar("hcMode").default("http"), + hcHostname: varchar("hcHostname"), + hcPort: integer("hcPort"), + hcInterval: integer("hcInterval").default(30), // in seconds + hcUnhealthyInterval: integer("hcUnhealthyInterval").default(30), // in seconds + hcTimeout: integer("hcTimeout").default(5), // in seconds + hcHeaders: varchar("hcHeaders"), + hcFollowRedirects: boolean("hcFollowRedirects").default(true), + hcMethod: varchar("hcMethod").default("GET"), + hcStatus: integer("hcStatus"), // http code + hcHealth: text("hcHealth").default("unknown") // "unknown", "healthy", "unhealthy" +}); + export const exitNodes = pgTable("exitNodes", { exitNodeId: serial("exitNodeId").primaryKey(), name: varchar("name").notNull(), @@ -689,3 +710,4 @@ export type OrgDomains = InferSelectModel; export type SiteResource = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; +export type TargetHealthCheck = InferSelectModel; \ No newline at end of file diff --git a/server/db/private/rateLimit.test.ts b/server/db/private/rateLimit.test.ts new file mode 100644 index 00000000..59952c8c --- /dev/null +++ b/server/db/private/rateLimit.test.ts @@ -0,0 +1,202 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +// Simple test file for the rate limit service with Redis +// Run with: npx ts-node rateLimitService.test.ts + +import { RateLimitService } from './rateLimit'; + +function generateClientId() { + return 'client-' + Math.random().toString(36).substring(2, 15); +} + +async function runTests() { + console.log('Starting Rate Limit Service Tests...\n'); + + const rateLimitService = new RateLimitService(); + let testsPassed = 0; + let testsTotal = 0; + + // Helper function to run a test + async function test(name: string, testFn: () => Promise) { + testsTotal++; + try { + await testFn(); + console.log(`✅ ${name}`); + testsPassed++; + } catch (error) { + console.log(`❌ ${name}: ${error}`); + } + } + + // Helper function for assertions + function assert(condition: boolean, message: string) { + if (!condition) { + throw new Error(message); + } + } + + // Test 1: Basic rate limiting + await test('Should allow requests under the limit', async () => { + const clientId = generateClientId(); + const maxRequests = 5; + + for (let i = 0; i < maxRequests - 1; i++) { + const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(!result.isLimited, `Request ${i + 1} should be allowed`); + assert(result.totalHits === i + 1, `Expected ${i + 1} hits, got ${result.totalHits}`); + } + }); + + // Test 2: Rate limit blocking + await test('Should block requests over the limit', async () => { + const clientId = generateClientId(); + const maxRequests = 30; + + // Use up all allowed requests + for (let i = 0; i < maxRequests - 1; i++) { + const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(!result.isLimited, `Request ${i + 1} should be allowed`); + } + + // Next request should be blocked + const blockedResult = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(blockedResult.isLimited, 'Request should be blocked'); + assert(blockedResult.reason === 'global', 'Should be blocked for global reason'); + }); + + // Test 3: Message type limits + await test('Should handle message type limits', async () => { + const clientId = generateClientId(); + const globalMax = 10; + const messageTypeMax = 2; + + // Send messages of type 'ping' up to the limit + for (let i = 0; i < messageTypeMax - 1; i++) { + const result = await rateLimitService.checkRateLimit( + clientId, + 'ping', + globalMax, + messageTypeMax + ); + assert(!result.isLimited, `Ping message ${i + 1} should be allowed`); + } + + // Next 'ping' should be blocked + const blockedResult = await rateLimitService.checkRateLimit( + clientId, + 'ping', + globalMax, + messageTypeMax + ); + assert(blockedResult.isLimited, 'Ping message should be blocked'); + assert(blockedResult.reason === 'message_type:ping', 'Should be blocked for message type'); + + // Other message types should still work + const otherResult = await rateLimitService.checkRateLimit( + clientId, + 'pong', + globalMax, + messageTypeMax + ); + assert(!otherResult.isLimited, 'Pong message should be allowed'); + }); + + // Test 4: Reset functionality + await test('Should reset client correctly', async () => { + const clientId = generateClientId(); + const maxRequests = 3; + + // Use up some requests + await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + await rateLimitService.checkRateLimit(clientId, 'test', maxRequests); + + // Reset the client + await rateLimitService.resetKey(clientId); + + // Should be able to make fresh requests + const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(!result.isLimited, 'Request after reset should be allowed'); + assert(result.totalHits === 1, 'Should have 1 hit after reset'); + }); + + // Test 5: Different clients are independent + await test('Should handle different clients independently', async () => { + const client1 = generateClientId(); + const client2 = generateClientId(); + const maxRequests = 2; + + // Client 1 uses up their limit + await rateLimitService.checkRateLimit(client1, undefined, maxRequests); + await rateLimitService.checkRateLimit(client1, undefined, maxRequests); + const client1Blocked = await rateLimitService.checkRateLimit(client1, undefined, maxRequests); + assert(client1Blocked.isLimited, 'Client 1 should be blocked'); + + // Client 2 should still be able to make requests + const client2Result = await rateLimitService.checkRateLimit(client2, undefined, maxRequests); + assert(!client2Result.isLimited, 'Client 2 should not be blocked'); + assert(client2Result.totalHits === 1, 'Client 2 should have 1 hit'); + }); + + // Test 6: Decrement functionality + await test('Should decrement correctly', async () => { + const clientId = generateClientId(); + const maxRequests = 5; + + // Make some requests + await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + let result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(result.totalHits === 3, 'Should have 3 hits before decrement'); + + // Decrement + await rateLimitService.decrementRateLimit(clientId); + + // Next request should reflect the decrement + result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests); + assert(result.totalHits === 3, 'Should have 3 hits after decrement + increment'); + }); + + // Wait a moment for any pending Redis operations + console.log('\nWaiting for Redis sync...'); + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Force sync to test Redis integration + await test('Should sync to Redis', async () => { + await rateLimitService.forceSyncAllPendingData(); + // If this doesn't throw, Redis sync is working + assert(true, 'Redis sync completed'); + }); + + // Cleanup + await rateLimitService.cleanup(); + + // Results + console.log(`\n--- Test Results ---`); + console.log(`✅ Passed: ${testsPassed}/${testsTotal}`); + console.log(`❌ Failed: ${testsTotal - testsPassed}/${testsTotal}`); + + if (testsPassed === testsTotal) { + console.log('\n🎉 All tests passed!'); + process.exit(0); + } else { + console.log('\n💥 Some tests failed!'); + process.exit(1); + } +} + +// Run the tests +runTests().catch(error => { + console.error('Test runner error:', error); + process.exit(1); +}); \ No newline at end of file diff --git a/server/db/private/rateLimit.ts b/server/db/private/rateLimit.ts new file mode 100644 index 00000000..ff8589bc --- /dev/null +++ b/server/db/private/rateLimit.ts @@ -0,0 +1,458 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import logger from "@server/logger"; +import redisManager from "@server/db/private/redis"; +import { build } from "@server/build"; + +// Rate limiting configuration +export const RATE_LIMIT_WINDOW = 60; // 1 minute in seconds +export const RATE_LIMIT_MAX_REQUESTS = 100; +export const RATE_LIMIT_PER_MESSAGE_TYPE = 20; // Per message type limit within the window + +// Configuration for batched Redis sync +export const REDIS_SYNC_THRESHOLD = 15; // Sync to Redis every N messages +export const REDIS_SYNC_FORCE_INTERVAL = 30000; // Force sync every 30 seconds as backup + +interface RateLimitTracker { + count: number; + windowStart: number; + pendingCount: number; + lastSyncedCount: number; +} + +interface RateLimitResult { + isLimited: boolean; + reason?: string; + totalHits?: number; + resetTime?: Date; +} + +export class RateLimitService { + private localRateLimitTracker: Map = new Map(); + private localMessageTypeRateLimitTracker: Map = new Map(); + private cleanupInterval: NodeJS.Timeout | null = null; + private forceSyncInterval: NodeJS.Timeout | null = null; + + constructor() { + if (build == "oss") { + return; + } + + // Start cleanup and sync intervals + this.cleanupInterval = setInterval(() => { + this.cleanupLocalRateLimit().catch((error) => { + logger.error("Error during rate limit cleanup:", error); + }); + }, 60000); // Run cleanup every minute + + this.forceSyncInterval = setInterval(() => { + this.forceSyncAllPendingData().catch((error) => { + logger.error("Error during force sync:", error); + }); + }, REDIS_SYNC_FORCE_INTERVAL); + } + + // Redis keys + private getRateLimitKey(clientId: string): string { + return `ratelimit:${clientId}`; + } + + private getMessageTypeRateLimitKey(clientId: string, messageType: string): string { + return `ratelimit:${clientId}:${messageType}`; + } + + // Helper function to sync local rate limit data to Redis + private async syncRateLimitToRedis( + clientId: string, + tracker: RateLimitTracker + ): Promise { + if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return; + + try { + const currentTime = Math.floor(Date.now() / 1000); + const globalKey = this.getRateLimitKey(clientId); + + // Get current value and add pending count + const currentValue = await redisManager.hget( + globalKey, + currentTime.toString() + ); + const newValue = ( + parseInt(currentValue || "0") + tracker.pendingCount + ).toString(); + await redisManager.hset(globalKey, currentTime.toString(), newValue); + + // Set TTL using the client directly + if (redisManager.getClient()) { + await redisManager + .getClient() + .expire(globalKey, RATE_LIMIT_WINDOW + 10); + } + + // Update tracking + tracker.lastSyncedCount = tracker.count; + tracker.pendingCount = 0; + + logger.debug(`Synced global rate limit to Redis for client ${clientId}`); + } catch (error) { + logger.error("Failed to sync global rate limit to Redis:", error); + } + } + + private async syncMessageTypeRateLimitToRedis( + clientId: string, + messageType: string, + tracker: RateLimitTracker + ): Promise { + if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return; + + try { + const currentTime = Math.floor(Date.now() / 1000); + const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType); + + // Get current value and add pending count + const currentValue = await redisManager.hget( + messageTypeKey, + currentTime.toString() + ); + const newValue = ( + parseInt(currentValue || "0") + tracker.pendingCount + ).toString(); + await redisManager.hset( + messageTypeKey, + currentTime.toString(), + newValue + ); + + // Set TTL using the client directly + if (redisManager.getClient()) { + await redisManager + .getClient() + .expire(messageTypeKey, RATE_LIMIT_WINDOW + 10); + } + + // Update tracking + tracker.lastSyncedCount = tracker.count; + tracker.pendingCount = 0; + + logger.debug( + `Synced message type rate limit to Redis for client ${clientId}, type ${messageType}` + ); + } catch (error) { + logger.error("Failed to sync message type rate limit to Redis:", error); + } + } + + // Initialize local tracker from Redis data + private async initializeLocalTracker(clientId: string): Promise { + const currentTime = Math.floor(Date.now() / 1000); + const windowStart = currentTime - RATE_LIMIT_WINDOW; + + if (!redisManager.isRedisEnabled()) { + return { + count: 0, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: 0 + }; + } + + try { + const globalKey = this.getRateLimitKey(clientId); + const globalRateLimitData = await redisManager.hgetall(globalKey); + + let count = 0; + for (const [timestamp, countStr] of Object.entries(globalRateLimitData)) { + const time = parseInt(timestamp); + if (time >= windowStart) { + count += parseInt(countStr); + } + } + + return { + count, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: count + }; + } catch (error) { + logger.error("Failed to initialize global tracker from Redis:", error); + return { + count: 0, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: 0 + }; + } + } + + private async initializeMessageTypeTracker( + clientId: string, + messageType: string + ): Promise { + const currentTime = Math.floor(Date.now() / 1000); + const windowStart = currentTime - RATE_LIMIT_WINDOW; + + if (!redisManager.isRedisEnabled()) { + return { + count: 0, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: 0 + }; + } + + try { + const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType); + const messageTypeRateLimitData = await redisManager.hgetall(messageTypeKey); + + let count = 0; + for (const [timestamp, countStr] of Object.entries(messageTypeRateLimitData)) { + const time = parseInt(timestamp); + if (time >= windowStart) { + count += parseInt(countStr); + } + } + + return { + count, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: count + }; + } catch (error) { + logger.error("Failed to initialize message type tracker from Redis:", error); + return { + count: 0, + windowStart: currentTime, + pendingCount: 0, + lastSyncedCount: 0 + }; + } + } + + // Main rate limiting function + async checkRateLimit( + clientId: string, + messageType?: string, + maxRequests: number = RATE_LIMIT_MAX_REQUESTS, + messageTypeLimit: number = RATE_LIMIT_PER_MESSAGE_TYPE, + windowMs: number = RATE_LIMIT_WINDOW * 1000 + ): Promise { + const currentTime = Math.floor(Date.now() / 1000); + const windowStart = currentTime - Math.floor(windowMs / 1000); + + // Check global rate limit + let globalTracker = this.localRateLimitTracker.get(clientId); + + if (!globalTracker || globalTracker.windowStart < windowStart) { + // New window or first request - initialize from Redis if available + globalTracker = await this.initializeLocalTracker(clientId); + globalTracker.windowStart = currentTime; + this.localRateLimitTracker.set(clientId, globalTracker); + } + + // Increment global counters + globalTracker.count++; + globalTracker.pendingCount++; + this.localRateLimitTracker.set(clientId, globalTracker); + + // Check if global limit would be exceeded + if (globalTracker.count >= maxRequests) { + return { + isLimited: true, + reason: "global", + totalHits: globalTracker.count, + resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + }; + } + + // Sync to Redis if threshold reached + if (globalTracker.pendingCount >= REDIS_SYNC_THRESHOLD) { + this.syncRateLimitToRedis(clientId, globalTracker); + } + + // Check message type specific rate limit if messageType is provided + if (messageType) { + const messageTypeKey = `${clientId}:${messageType}`; + let messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey); + + if (!messageTypeTracker || messageTypeTracker.windowStart < windowStart) { + // New window or first request for this message type - initialize from Redis if available + messageTypeTracker = await this.initializeMessageTypeTracker(clientId, messageType); + messageTypeTracker.windowStart = currentTime; + this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker); + } + + // Increment message type counters + messageTypeTracker.count++; + messageTypeTracker.pendingCount++; + this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker); + + // Check if message type limit would be exceeded + if (messageTypeTracker.count >= messageTypeLimit) { + return { + isLimited: true, + reason: `message_type:${messageType}`, + totalHits: messageTypeTracker.count, + resetTime: new Date((messageTypeTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + }; + } + + // Sync to Redis if threshold reached + if (messageTypeTracker.pendingCount >= REDIS_SYNC_THRESHOLD) { + this.syncMessageTypeRateLimitToRedis(clientId, messageType, messageTypeTracker); + } + } + + return { + isLimited: false, + totalHits: globalTracker.count, + resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000) + }; + } + + // Decrement function for skipSuccessfulRequests/skipFailedRequests functionality + async decrementRateLimit(clientId: string, messageType?: string): Promise { + // Decrement global counter + const globalTracker = this.localRateLimitTracker.get(clientId); + if (globalTracker && globalTracker.count > 0) { + globalTracker.count--; + // We need to account for this in pending count to sync correctly + globalTracker.pendingCount--; + } + + // Decrement message type counter if provided + if (messageType) { + const messageTypeKey = `${clientId}:${messageType}`; + const messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey); + if (messageTypeTracker && messageTypeTracker.count > 0) { + messageTypeTracker.count--; + messageTypeTracker.pendingCount--; + } + } + } + + // Reset key function + async resetKey(clientId: string): Promise { + // Remove from local tracking + this.localRateLimitTracker.delete(clientId); + + // Remove all message type entries for this client + for (const [key] of this.localMessageTypeRateLimitTracker) { + if (key.startsWith(`${clientId}:`)) { + this.localMessageTypeRateLimitTracker.delete(key); + } + } + + // Remove from Redis if enabled + if (redisManager.isRedisEnabled()) { + const globalKey = this.getRateLimitKey(clientId); + await redisManager.del(globalKey); + + // Get all message type keys for this client and delete them + const client = redisManager.getClient(); + if (client) { + const messageTypeKeys = await client.keys(`ratelimit:${clientId}:*`); + if (messageTypeKeys.length > 0) { + await Promise.all(messageTypeKeys.map(key => redisManager.del(key))); + } + } + } + } + + // Cleanup old local rate limit entries and force sync pending data + private async cleanupLocalRateLimit(): Promise { + const currentTime = Math.floor(Date.now() / 1000); + const windowStart = currentTime - RATE_LIMIT_WINDOW; + + // Clean up global rate limit tracking and sync pending data + for (const [clientId, tracker] of this.localRateLimitTracker.entries()) { + if (tracker.windowStart < windowStart) { + // Sync any pending data before cleanup + if (tracker.pendingCount > 0) { + await this.syncRateLimitToRedis(clientId, tracker); + } + this.localRateLimitTracker.delete(clientId); + } + } + + // Clean up message type rate limit tracking and sync pending data + for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) { + if (tracker.windowStart < windowStart) { + // Sync any pending data before cleanup + if (tracker.pendingCount > 0) { + const [clientId, messageType] = key.split(":", 2); + await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker); + } + this.localMessageTypeRateLimitTracker.delete(key); + } + } + } + + // Force sync all pending rate limit data to Redis + async forceSyncAllPendingData(): Promise { + if (!redisManager.isRedisEnabled()) return; + + logger.debug("Force syncing all pending rate limit data to Redis..."); + + // Sync all pending global rate limits + for (const [clientId, tracker] of this.localRateLimitTracker.entries()) { + if (tracker.pendingCount > 0) { + await this.syncRateLimitToRedis(clientId, tracker); + } + } + + // Sync all pending message type rate limits + for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) { + if (tracker.pendingCount > 0) { + const [clientId, messageType] = key.split(":", 2); + await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker); + } + } + + logger.debug("Completed force sync of pending rate limit data"); + } + + // Cleanup function for graceful shutdown + async cleanup(): Promise { + if (build == "oss") { + return; + } + + // Clear intervals + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + } + if (this.forceSyncInterval) { + clearInterval(this.forceSyncInterval); + } + + // Force sync all pending data + await this.forceSyncAllPendingData(); + + // Clear local data + this.localRateLimitTracker.clear(); + this.localMessageTypeRateLimitTracker.clear(); + + logger.info("Rate limit service cleanup completed"); + } +} + +// Export singleton instance +export const rateLimitService = new RateLimitService(); + +// Handle process termination +process.on("SIGTERM", () => rateLimitService.cleanup()); +process.on("SIGINT", () => rateLimitService.cleanup()); \ No newline at end of file diff --git a/server/db/private/redis.ts b/server/db/private/redis.ts new file mode 100644 index 00000000..b1f01a49 --- /dev/null +++ b/server/db/private/redis.ts @@ -0,0 +1,782 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Redis, { RedisOptions } from "ioredis"; +import logger from "@server/logger"; +import config from "@server/lib/config"; +import { build } from "@server/build"; + +class RedisManager { + public client: Redis | null = null; + private writeClient: Redis | null = null; // Master for writes + private readClient: Redis | null = null; // Replica for reads + private subscriber: Redis | null = null; + private publisher: Redis | null = null; + private isEnabled: boolean = false; + private isHealthy: boolean = true; + private isWriteHealthy: boolean = true; + private isReadHealthy: boolean = true; + private lastHealthCheck: number = 0; + private healthCheckInterval: number = 30000; // 30 seconds + private connectionTimeout: number = 15000; // 15 seconds + private commandTimeout: number = 15000; // 15 seconds + private hasReplicas: boolean = false; + private maxRetries: number = 3; + private baseRetryDelay: number = 100; // 100ms + private maxRetryDelay: number = 2000; // 2 seconds + private backoffMultiplier: number = 2; + private subscribers: Map< + string, + Set<(channel: string, message: string) => void> + > = new Map(); + private reconnectionCallbacks: Set<() => Promise> = new Set(); + + constructor() { + if (build == "oss") { + this.isEnabled = false; + return + } + this.isEnabled = config.getRawPrivateConfig().flags?.enable_redis || false; + if (this.isEnabled) { + this.initializeClients(); + } + } + + // Register callback to be called when Redis reconnects + public onReconnection(callback: () => Promise): void { + this.reconnectionCallbacks.add(callback); + } + + // Unregister reconnection callback + public offReconnection(callback: () => Promise): void { + this.reconnectionCallbacks.delete(callback); + } + + private async triggerReconnectionCallbacks(): Promise { + logger.info(`Triggering ${this.reconnectionCallbacks.size} reconnection callbacks`); + + const promises = Array.from(this.reconnectionCallbacks).map(async (callback) => { + try { + await callback(); + } catch (error) { + logger.error("Error in reconnection callback:", error); + } + }); + + await Promise.allSettled(promises); + } + + private async resubscribeToChannels(): Promise { + if (!this.subscriber || this.subscribers.size === 0) return; + + logger.info(`Re-subscribing to ${this.subscribers.size} channels after Redis reconnection`); + + try { + const channels = Array.from(this.subscribers.keys()); + if (channels.length > 0) { + await this.subscriber.subscribe(...channels); + logger.info(`Successfully re-subscribed to channels: ${channels.join(', ')}`); + } + } catch (error) { + logger.error("Failed to re-subscribe to channels:", error); + } + } + + private getRedisConfig(): RedisOptions { + const redisConfig = config.getRawPrivateConfig().redis!; + const opts: RedisOptions = { + host: redisConfig.host!, + port: redisConfig.port!, + password: redisConfig.password, + db: redisConfig.db, + // tls: { + // rejectUnauthorized: + // redisConfig.tls?.reject_unauthorized || false + // } + }; + return opts; + } + + private getReplicaRedisConfig(): RedisOptions | null { + const redisConfig = config.getRawPrivateConfig().redis!; + if (!redisConfig.replicas || redisConfig.replicas.length === 0) { + return null; + } + + // Use the first replica for simplicity + // In production, you might want to implement load balancing across replicas + const replica = redisConfig.replicas[0]; + const opts: RedisOptions = { + host: replica.host!, + port: replica.port!, + password: replica.password, + db: replica.db || redisConfig.db, + // tls: { + // rejectUnauthorized: + // replica.tls?.reject_unauthorized || false + // } + }; + return opts; + } + + // Add reconnection logic in initializeClients + private initializeClients(): void { + const masterConfig = this.getRedisConfig(); + const replicaConfig = this.getReplicaRedisConfig(); + + this.hasReplicas = replicaConfig !== null; + + try { + // Initialize master connection for writes + this.writeClient = new Redis({ + ...masterConfig, + enableReadyCheck: false, + maxRetriesPerRequest: 3, + keepAlive: 30000, + connectTimeout: this.connectionTimeout, + commandTimeout: this.commandTimeout, + }); + + // Initialize replica connection for reads (if available) + if (this.hasReplicas) { + this.readClient = new Redis({ + ...replicaConfig!, + enableReadyCheck: false, + maxRetriesPerRequest: 3, + keepAlive: 30000, + connectTimeout: this.connectionTimeout, + commandTimeout: this.commandTimeout, + }); + } else { + // Fallback to master for reads if no replicas + this.readClient = this.writeClient; + } + + // Backward compatibility - point to write client + this.client = this.writeClient; + + // Publisher uses master (writes) + this.publisher = new Redis({ + ...masterConfig, + enableReadyCheck: false, + maxRetriesPerRequest: 3, + keepAlive: 30000, + connectTimeout: this.connectionTimeout, + commandTimeout: this.commandTimeout, + }); + + // Subscriber uses replica if available (reads) + this.subscriber = new Redis({ + ...(this.hasReplicas ? replicaConfig! : masterConfig), + enableReadyCheck: false, + maxRetriesPerRequest: 3, + keepAlive: 30000, + connectTimeout: this.connectionTimeout, + commandTimeout: this.commandTimeout, + }); + + // Add reconnection handlers for write client + this.writeClient.on("error", (err) => { + logger.error("Redis write client error:", err); + this.isWriteHealthy = false; + this.isHealthy = false; + }); + + this.writeClient.on("reconnecting", () => { + logger.info("Redis write client reconnecting..."); + this.isWriteHealthy = false; + this.isHealthy = false; + }); + + this.writeClient.on("ready", () => { + logger.info("Redis write client ready"); + this.isWriteHealthy = true; + this.updateOverallHealth(); + + // Trigger reconnection callbacks when Redis comes back online + if (this.isHealthy) { + this.triggerReconnectionCallbacks().catch(error => { + logger.error("Error triggering reconnection callbacks:", error); + }); + } + }); + + this.writeClient.on("connect", () => { + logger.info("Redis write client connected"); + }); + + // Add reconnection handlers for read client (if different from write) + if (this.hasReplicas && this.readClient !== this.writeClient) { + this.readClient.on("error", (err) => { + logger.error("Redis read client error:", err); + this.isReadHealthy = false; + this.updateOverallHealth(); + }); + + this.readClient.on("reconnecting", () => { + logger.info("Redis read client reconnecting..."); + this.isReadHealthy = false; + this.updateOverallHealth(); + }); + + this.readClient.on("ready", () => { + logger.info("Redis read client ready"); + this.isReadHealthy = true; + this.updateOverallHealth(); + + // Trigger reconnection callbacks when Redis comes back online + if (this.isHealthy) { + this.triggerReconnectionCallbacks().catch(error => { + logger.error("Error triggering reconnection callbacks:", error); + }); + } + }); + + this.readClient.on("connect", () => { + logger.info("Redis read client connected"); + }); + } else { + // If using same client for reads and writes + this.isReadHealthy = this.isWriteHealthy; + } + + this.publisher.on("error", (err) => { + logger.error("Redis publisher error:", err); + }); + + this.publisher.on("ready", () => { + logger.info("Redis publisher ready"); + }); + + this.publisher.on("connect", () => { + logger.info("Redis publisher connected"); + }); + + this.subscriber.on("error", (err) => { + logger.error("Redis subscriber error:", err); + }); + + this.subscriber.on("ready", () => { + logger.info("Redis subscriber ready"); + // Re-subscribe to all channels after reconnection + this.resubscribeToChannels().catch((error: any) => { + logger.error("Error re-subscribing to channels:", error); + }); + }); + + this.subscriber.on("connect", () => { + logger.info("Redis subscriber connected"); + }); + + // Set up message handler for subscriber + this.subscriber.on( + "message", + (channel: string, message: string) => { + const channelSubscribers = this.subscribers.get(channel); + if (channelSubscribers) { + channelSubscribers.forEach((callback) => { + try { + callback(channel, message); + } catch (error) { + logger.error( + `Error in subscriber callback for channel ${channel}:`, + error + ); + } + }); + } + } + ); + + const setupMessage = this.hasReplicas + ? "Redis clients initialized successfully with replica support" + : "Redis clients initialized successfully (single instance)"; + logger.info(setupMessage); + + // Start periodic health monitoring + this.startHealthMonitoring(); + } catch (error) { + logger.error("Failed to initialize Redis clients:", error); + this.isEnabled = false; + } + } + + private updateOverallHealth(): void { + // Overall health is true if write is healthy and (read is healthy OR we don't have replicas) + this.isHealthy = this.isWriteHealthy && (this.isReadHealthy || !this.hasReplicas); + } + + private async executeWithRetry( + operation: () => Promise, + operationName: string, + fallbackOperation?: () => Promise + ): Promise { + let lastError: Error | null = null; + + for (let attempt = 0; attempt <= this.maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error as Error; + + // If this is the last attempt, try fallback if available + if (attempt === this.maxRetries && fallbackOperation) { + try { + logger.warn(`${operationName} primary operation failed, trying fallback`); + return await fallbackOperation(); + } catch (fallbackError) { + logger.error(`${operationName} fallback also failed:`, fallbackError); + throw lastError; + } + } + + // Don't retry on the last attempt + if (attempt === this.maxRetries) { + break; + } + + // Calculate delay with exponential backoff + const delay = Math.min( + this.baseRetryDelay * Math.pow(this.backoffMultiplier, attempt), + this.maxRetryDelay + ); + + logger.warn(`${operationName} failed (attempt ${attempt + 1}/${this.maxRetries + 1}), retrying in ${delay}ms:`, error); + + // Wait before retrying + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + + logger.error(`${operationName} failed after ${this.maxRetries + 1} attempts:`, lastError); + throw lastError; + } + + private startHealthMonitoring(): void { + if (!this.isEnabled) return; + + // Check health every 30 seconds + setInterval(async () => { + try { + await this.checkRedisHealth(); + } catch (error) { + logger.error("Error during Redis health monitoring:", error); + } + }, this.healthCheckInterval); + } + + public isRedisEnabled(): boolean { + return this.isEnabled && this.client !== null && this.isHealthy; + } + + private async checkRedisHealth(): Promise { + const now = Date.now(); + + // Only check health every 30 seconds + if (now - this.lastHealthCheck < this.healthCheckInterval) { + return this.isHealthy; + } + + this.lastHealthCheck = now; + + if (!this.writeClient) { + this.isHealthy = false; + this.isWriteHealthy = false; + this.isReadHealthy = false; + return false; + } + + try { + // Check write client (master) health + await Promise.race([ + this.writeClient.ping(), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Write client health check timeout')), 2000) + ) + ]); + this.isWriteHealthy = true; + + // Check read client health if it's different from write client + if (this.hasReplicas && this.readClient && this.readClient !== this.writeClient) { + try { + await Promise.race([ + this.readClient.ping(), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Read client health check timeout')), 2000) + ) + ]); + this.isReadHealthy = true; + } catch (error) { + logger.error("Redis read client health check failed:", error); + this.isReadHealthy = false; + } + } else { + this.isReadHealthy = this.isWriteHealthy; + } + + this.updateOverallHealth(); + return this.isHealthy; + } catch (error) { + logger.error("Redis write client health check failed:", error); + this.isWriteHealthy = false; + this.isReadHealthy = false; // If write fails, consider read as failed too for safety + this.isHealthy = false; + return false; + } + } + + public getClient(): Redis { + return this.client!; + } + + public getWriteClient(): Redis | null { + return this.writeClient; + } + + public getReadClient(): Redis | null { + return this.readClient; + } + + public hasReplicaSupport(): boolean { + return this.hasReplicas; + } + + public getHealthStatus(): { + isEnabled: boolean; + isHealthy: boolean; + isWriteHealthy: boolean; + isReadHealthy: boolean; + hasReplicas: boolean; + } { + return { + isEnabled: this.isEnabled, + isHealthy: this.isHealthy, + isWriteHealthy: this.isWriteHealthy, + isReadHealthy: this.isReadHealthy, + hasReplicas: this.hasReplicas + }; + } + + public async set( + key: string, + value: string, + ttl?: number + ): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + async () => { + if (ttl) { + await this.writeClient!.setex(key, ttl, value); + } else { + await this.writeClient!.set(key, value); + } + }, + "Redis SET" + ); + return true; + } catch (error) { + logger.error("Redis SET error:", error); + return false; + } + } + + public async get(key: string): Promise { + if (!this.isRedisEnabled() || !this.readClient) return null; + + try { + const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) + ? () => this.writeClient!.get(key) + : undefined; + + return await this.executeWithRetry( + () => this.readClient!.get(key), + "Redis GET", + fallbackOperation + ); + } catch (error) { + logger.error("Redis GET error:", error); + return null; + } + } + + public async del(key: string): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + () => this.writeClient!.del(key), + "Redis DEL" + ); + return true; + } catch (error) { + logger.error("Redis DEL error:", error); + return false; + } + } + + public async sadd(key: string, member: string): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + () => this.writeClient!.sadd(key, member), + "Redis SADD" + ); + return true; + } catch (error) { + logger.error("Redis SADD error:", error); + return false; + } + } + + public async srem(key: string, member: string): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + () => this.writeClient!.srem(key, member), + "Redis SREM" + ); + return true; + } catch (error) { + logger.error("Redis SREM error:", error); + return false; + } + } + + public async smembers(key: string): Promise { + if (!this.isRedisEnabled() || !this.readClient) return []; + + try { + const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) + ? () => this.writeClient!.smembers(key) + : undefined; + + return await this.executeWithRetry( + () => this.readClient!.smembers(key), + "Redis SMEMBERS", + fallbackOperation + ); + } catch (error) { + logger.error("Redis SMEMBERS error:", error); + return []; + } + } + + public async hset( + key: string, + field: string, + value: string + ): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + () => this.writeClient!.hset(key, field, value), + "Redis HSET" + ); + return true; + } catch (error) { + logger.error("Redis HSET error:", error); + return false; + } + } + + public async hget(key: string, field: string): Promise { + if (!this.isRedisEnabled() || !this.readClient) return null; + + try { + const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) + ? () => this.writeClient!.hget(key, field) + : undefined; + + return await this.executeWithRetry( + () => this.readClient!.hget(key, field), + "Redis HGET", + fallbackOperation + ); + } catch (error) { + logger.error("Redis HGET error:", error); + return null; + } + } + + public async hdel(key: string, field: string): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + + try { + await this.executeWithRetry( + () => this.writeClient!.hdel(key, field), + "Redis HDEL" + ); + return true; + } catch (error) { + logger.error("Redis HDEL error:", error); + return false; + } + } + + public async hgetall(key: string): Promise> { + if (!this.isRedisEnabled() || !this.readClient) return {}; + + try { + const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy) + ? () => this.writeClient!.hgetall(key) + : undefined; + + return await this.executeWithRetry( + () => this.readClient!.hgetall(key), + "Redis HGETALL", + fallbackOperation + ); + } catch (error) { + logger.error("Redis HGETALL error:", error); + return {}; + } + } + + public async publish(channel: string, message: string): Promise { + if (!this.isRedisEnabled() || !this.publisher) return false; + + // Quick health check before attempting to publish + const isHealthy = await this.checkRedisHealth(); + if (!isHealthy) { + logger.warn("Skipping Redis publish due to unhealthy connection"); + return false; + } + + try { + await this.executeWithRetry( + async () => { + // Add timeout to prevent hanging + return Promise.race([ + this.publisher!.publish(channel, message), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Redis publish timeout')), 3000) + ) + ]); + }, + "Redis PUBLISH" + ); + return true; + } catch (error) { + logger.error("Redis PUBLISH error:", error); + this.isHealthy = false; // Mark as unhealthy on error + return false; + } + } + + public async subscribe( + channel: string, + callback: (channel: string, message: string) => void + ): Promise { + if (!this.isRedisEnabled() || !this.subscriber) return false; + + try { + // Add callback to subscribers map + if (!this.subscribers.has(channel)) { + this.subscribers.set(channel, new Set()); + // Only subscribe to the channel if it's the first subscriber + await this.executeWithRetry( + async () => { + return Promise.race([ + this.subscriber!.subscribe(channel), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Redis subscribe timeout')), 5000) + ) + ]); + }, + "Redis SUBSCRIBE" + ); + } + + this.subscribers.get(channel)!.add(callback); + return true; + } catch (error) { + logger.error("Redis SUBSCRIBE error:", error); + this.isHealthy = false; + return false; + } + } + + public async unsubscribe( + channel: string, + callback?: (channel: string, message: string) => void + ): Promise { + if (!this.isRedisEnabled() || !this.subscriber) return false; + + try { + const channelSubscribers = this.subscribers.get(channel); + if (!channelSubscribers) return true; + + if (callback) { + // Remove specific callback + channelSubscribers.delete(callback); + if (channelSubscribers.size === 0) { + this.subscribers.delete(channel); + await this.executeWithRetry( + () => this.subscriber!.unsubscribe(channel), + "Redis UNSUBSCRIBE" + ); + } + } else { + // Remove all callbacks for this channel + this.subscribers.delete(channel); + await this.executeWithRetry( + () => this.subscriber!.unsubscribe(channel), + "Redis UNSUBSCRIBE" + ); + } + + return true; + } catch (error) { + logger.error("Redis UNSUBSCRIBE error:", error); + return false; + } + } + + public async disconnect(): Promise { + try { + if (this.client) { + await this.client.quit(); + this.client = null; + } + if (this.writeClient) { + await this.writeClient.quit(); + this.writeClient = null; + } + if (this.readClient && this.readClient !== this.writeClient) { + await this.readClient.quit(); + this.readClient = null; + } + if (this.publisher) { + await this.publisher.quit(); + this.publisher = null; + } + if (this.subscriber) { + await this.subscriber.quit(); + this.subscriber = null; + } + this.subscribers.clear(); + logger.info("Redis clients disconnected"); + } catch (error) { + logger.error("Error disconnecting Redis clients:", error); + } + } +} + +export const redisManager = new RedisManager(); +export const redis = redisManager.getClient(); +export default redisManager; diff --git a/server/db/private/redisStore.ts b/server/db/private/redisStore.ts new file mode 100644 index 00000000..235f8f8f --- /dev/null +++ b/server/db/private/redisStore.ts @@ -0,0 +1,223 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Store, Options, IncrementResponse } from 'express-rate-limit'; +import { rateLimitService } from './rateLimit'; +import logger from '@server/logger'; + +/** + * A Redis-backed rate limiting store for express-rate-limit that optimizes + * for local read performance and batched writes to Redis. + * + * This store uses the same optimized rate limiting logic as the WebSocket + * implementation, providing: + * - Local caching for fast reads + * - Batched writes to Redis to reduce load + * - Automatic cleanup of expired entries + * - Graceful fallback when Redis is unavailable + */ +export default class RedisStore implements Store { + /** + * The duration of time before which all hit counts are reset (in milliseconds). + */ + windowMs!: number; + + /** + * Maximum number of requests allowed within the window. + */ + max!: number; + + /** + * Optional prefix for Redis keys to avoid collisions. + */ + prefix: string; + + /** + * Whether to skip incrementing on failed requests. + */ + skipFailedRequests: boolean; + + /** + * Whether to skip incrementing on successful requests. + */ + skipSuccessfulRequests: boolean; + + /** + * @constructor for RedisStore. + * + * @param options - Configuration options for the store. + */ + constructor(options: { + prefix?: string; + skipFailedRequests?: boolean; + skipSuccessfulRequests?: boolean; + } = {}) { + this.prefix = options.prefix || 'express-rate-limit'; + this.skipFailedRequests = options.skipFailedRequests || false; + this.skipSuccessfulRequests = options.skipSuccessfulRequests || false; + } + + /** + * Method that actually initializes the store. Must be synchronous. + * + * @param options - The options used to setup express-rate-limit. + */ + init(options: Options): void { + this.windowMs = options.windowMs; + this.max = options.max as number; + + // logger.debug(`RedisStore initialized with windowMs: ${this.windowMs}, max: ${this.max}, prefix: ${this.prefix}`); + } + + /** + * Method to increment a client's hit counter. + * + * @param key - The identifier for a client (usually IP address). + * @returns Promise resolving to the number of hits and reset time for that client. + */ + async increment(key: string): Promise { + try { + const clientId = `${this.prefix}:${key}`; + + const result = await rateLimitService.checkRateLimit( + clientId, + undefined, // No message type for HTTP requests + this.max, + undefined, // No message type limit + this.windowMs + ); + + // logger.debug(`Incremented rate limit for key: ${key} with max: ${this.max}, totalHits: ${result.totalHits}`); + + return { + totalHits: result.totalHits || 1, + resetTime: result.resetTime || new Date(Date.now() + this.windowMs) + }; + } catch (error) { + logger.error(`RedisStore increment error for key ${key}:`, error); + + // Return safe defaults on error to prevent blocking requests + return { + totalHits: 1, + resetTime: new Date(Date.now() + this.windowMs) + }; + } + } + + /** + * Method to decrement a client's hit counter. + * Used when skipSuccessfulRequests or skipFailedRequests is enabled. + * + * @param key - The identifier for a client. + */ + async decrement(key: string): Promise { + try { + const clientId = `${this.prefix}:${key}`; + await rateLimitService.decrementRateLimit(clientId); + + // logger.debug(`Decremented rate limit for key: ${key}`); + } catch (error) { + logger.error(`RedisStore decrement error for key ${key}:`, error); + // Don't throw - decrement failures shouldn't block requests + } + } + + /** + * Method to reset a client's hit counter. + * + * @param key - The identifier for a client. + */ + async resetKey(key: string): Promise { + try { + const clientId = `${this.prefix}:${key}`; + await rateLimitService.resetKey(clientId); + + // logger.debug(`Reset rate limit for key: ${key}`); + } catch (error) { + logger.error(`RedisStore resetKey error for key ${key}:`, error); + // Don't throw - reset failures shouldn't block requests + } + } + + /** + * Method to reset everyone's hit counter. + * + * This method is optional and is never called by express-rate-limit. + * We implement it for completeness but it's not recommended for production use + * as it could be expensive with large datasets. + */ + async resetAll(): Promise { + try { + logger.warn('RedisStore resetAll called - this operation can be expensive'); + + // Force sync all pending data first + await rateLimitService.forceSyncAllPendingData(); + + // Note: We don't actually implement full reset as it would require + // scanning all Redis keys with our prefix, which could be expensive. + // In production, it's better to let entries expire naturally. + + logger.info('RedisStore resetAll completed (pending data synced)'); + } catch (error) { + logger.error('RedisStore resetAll error:', error); + // Don't throw - this is an optional method + } + } + + /** + * Get current hit count for a key without incrementing. + * This is a custom method not part of the Store interface. + * + * @param key - The identifier for a client. + * @returns Current hit count and reset time, or null if no data exists. + */ + async getHits(key: string): Promise<{ totalHits: number; resetTime: Date } | null> { + try { + const clientId = `${this.prefix}:${key}`; + + // Use checkRateLimit with max + 1 to avoid actually incrementing + // but still get the current count + const result = await rateLimitService.checkRateLimit( + clientId, + undefined, + this.max + 1000, // Set artificially high to avoid triggering limit + undefined, + this.windowMs + ); + + // Decrement since we don't actually want to count this check + await rateLimitService.decrementRateLimit(clientId); + + return { + totalHits: Math.max(0, (result.totalHits || 0) - 1), // Adjust for the decrement + resetTime: result.resetTime || new Date(Date.now() + this.windowMs) + }; + } catch (error) { + logger.error(`RedisStore getHits error for key ${key}:`, error); + return null; + } + } + + /** + * Cleanup method for graceful shutdown. + * This is not part of the Store interface but is useful for cleanup. + */ + async shutdown(): Promise { + try { + // The rateLimitService handles its own cleanup + logger.info('RedisStore shutdown completed'); + } catch (error) { + logger.error('RedisStore shutdown error:', error); + } + } +} diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts index 728880f2..f7719c50 100644 --- a/server/db/queries/verifySessionQueries.ts +++ b/server/db/queries/verifySessionQueries.ts @@ -1,4 +1,4 @@ -import { db } from "@server/db"; +import { db, loginPage, LoginPage, loginPageOrg } from "@server/db"; import { Resource, ResourcePassword, @@ -39,7 +39,10 @@ export async function getResourceByDomain( ): Promise { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/domain/${domain}`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/domain/${domain}`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -91,7 +94,10 @@ export async function getUserSessionWithUser( ): Promise { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/session/${userSessionId}`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/session/${userSessionId}`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -132,7 +138,10 @@ export async function getUserSessionWithUser( export async function getUserOrgRole(userId: string, orgId: string) { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/org/${orgId}/role`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/org/${orgId}/role`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -154,12 +163,7 @@ export async function getUserOrgRole(userId: string, orgId: string) { const userOrgRole = await db .select() .from(userOrgs) - .where( - and( - eq(userOrgs.userId, userId), - eq(userOrgs.orgId, orgId) - ) - ) + .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId))) .limit(1); return userOrgRole.length > 0 ? userOrgRole[0] : null; @@ -168,10 +172,16 @@ export async function getUserOrgRole(userId: string, orgId: string) { /** * Check if role has access to resource */ -export async function getRoleResourceAccess(resourceId: number, roleId: number) { +export async function getRoleResourceAccess( + resourceId: number, + roleId: number +) { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/role/${roleId}/resource/${resourceId}/access`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/role/${roleId}/resource/${resourceId}/access`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -207,10 +217,16 @@ export async function getRoleResourceAccess(resourceId: number, roleId: number) /** * Check if user has direct access to resource */ -export async function getUserResourceAccess(userId: string, resourceId: number) { +export async function getUserResourceAccess( + userId: string, + resourceId: number +) { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/resource/${resourceId}/access`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/resource/${resourceId}/access`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -246,10 +262,15 @@ export async function getUserResourceAccess(userId: string, resourceId: number) /** * Get resource rules for a given resource */ -export async function getResourceRules(resourceId: number): Promise { +export async function getResourceRules( + resourceId: number +): Promise { if (config.isManagedMode()) { try { - const response = await axios.get(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/rules`, await tokenManager.getAuthHeader()); + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/rules`, + await tokenManager.getAuthHeader() + ); return response.data.data; } catch (error) { if (axios.isAxiosError(error)) { @@ -275,3 +296,50 @@ export async function getResourceRules(resourceId: number): Promise { + if (config.isManagedMode()) { + try { + const response = await axios.get( + `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/org/${orgId}/login-page`, + await tokenManager.getAuthHeader() + ); + return response.data.data; + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error("Error fetching config in verify session:", { + message: error.message, + code: error.code, + status: error.response?.status, + statusText: error.response?.statusText, + url: error.config?.url, + method: error.config?.method + }); + } else { + logger.error("Error fetching config in verify session:", error); + } + return null; + } + } + + const [result] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)) + .innerJoin( + loginPage, + eq(loginPageOrg.loginPageId, loginPage.loginPageId) + ) + .limit(1); + + if (!result) { + return null; + } + + return result?.loginPage; +} diff --git a/server/db/sqlite/index.ts b/server/db/sqlite/index.ts index 9ad4678c..8c7a15e5 100644 --- a/server/db/sqlite/index.ts +++ b/server/db/sqlite/index.ts @@ -1,2 +1,3 @@ export * from "./driver"; export * from "./schema"; +export * from "./privateSchema"; \ No newline at end of file diff --git a/server/db/sqlite/privateSchema.ts b/server/db/sqlite/privateSchema.ts new file mode 100644 index 00000000..fbe86e25 --- /dev/null +++ b/server/db/sqlite/privateSchema.ts @@ -0,0 +1,239 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + sqliteTable, + integer, + text, + real +} from "drizzle-orm/sqlite-core"; +import { InferSelectModel } from "drizzle-orm"; +import { domains, orgs, targets, users, exitNodes, sessions } 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 }), + domain: text("domain").notNull(), + token: text("token").notNull(), + keyAuthorization: text("keyAuthorization").notNull(), + createdAt: integer("createdAt").notNull(), + expiresAt: integer("expiresAt").notNull(), + completed: integer("completed", { mode: "boolean" }).default(false) +}); + +export const account = sqliteTable("account", { + accountId: integer("accountId").primaryKey({ autoIncrement: true }), + userId: text("userId") + .notNull() + .references(() => users.userId, { onDelete: "cascade" }) +}); + +export const customers = sqliteTable("customers", { + customerId: text("customerId").primaryKey().notNull(), + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + // accountId: integer("accountId") + // .references(() => account.accountId, { onDelete: "cascade" }), // Optional, if using accounts + email: text("email"), + name: text("name"), + phone: text("phone"), + address: text("address"), + createdAt: integer("createdAt").notNull(), + updatedAt: integer("updatedAt").notNull() +}); + +export const subscriptions = sqliteTable("subscriptions", { + subscriptionId: text("subscriptionId") + .primaryKey() + .notNull(), + customerId: text("customerId") + .notNull() + .references(() => customers.customerId, { onDelete: "cascade" }), + status: text("status").notNull().default("active"), // active, past_due, canceled, unpaid + canceledAt: integer("canceledAt"), + createdAt: integer("createdAt").notNull(), + updatedAt: integer("updatedAt"), + billingCycleAnchor: integer("billingCycleAnchor") +}); + +export const subscriptionItems = sqliteTable("subscriptionItems", { + subscriptionItemId: integer("subscriptionItemId").primaryKey({ autoIncrement: true }), + subscriptionId: text("subscriptionId") + .notNull() + .references(() => subscriptions.subscriptionId, { + onDelete: "cascade" + }), + planId: text("planId").notNull(), + priceId: text("priceId"), + meterId: text("meterId"), + unitAmount: real("unitAmount"), + tiers: text("tiers"), + interval: text("interval"), + currentPeriodStart: integer("currentPeriodStart"), + currentPeriodEnd: integer("currentPeriodEnd"), + name: text("name") +}); + +export const accountDomains = sqliteTable("accountDomains", { + accountId: integer("accountId") + .notNull() + .references(() => account.accountId, { onDelete: "cascade" }), + domainId: text("domainId") + .notNull() + .references(() => domains.domainId, { onDelete: "cascade" }) +}); + +export const usage = sqliteTable("usage", { + usageId: text("usageId").primaryKey(), + featureId: text("featureId").notNull(), + orgId: text("orgId") + .references(() => orgs.orgId, { onDelete: "cascade" }) + .notNull(), + meterId: text("meterId"), + instantaneousValue: real("instantaneousValue"), + latestValue: real("latestValue").notNull(), + previousValue: real("previousValue"), + updatedAt: integer("updatedAt").notNull(), + rolledOverAt: integer("rolledOverAt"), + nextRolloverAt: integer("nextRolloverAt") +}); + +export const limits = sqliteTable("limits", { + limitId: text("limitId").primaryKey(), + featureId: text("featureId").notNull(), + orgId: text("orgId") + .references(() => orgs.orgId, { + onDelete: "cascade" + }) + .notNull(), + value: real("value"), + description: text("description") +}); + +export const usageNotifications = sqliteTable("usageNotifications", { + notificationId: integer("notificationId").primaryKey({ autoIncrement: true }), + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }), + featureId: text("featureId").notNull(), + limitId: text("limitId").notNull(), + notificationType: text("notificationType").notNull(), + sentAt: integer("sentAt").notNull() +}); + +export const domainNamespaces = sqliteTable("domainNamespaces", { + domainNamespaceId: text("domainNamespaceId").primaryKey(), + domainId: text("domainId") + .references(() => domains.domainId, { + onDelete: "set null" + }) + .notNull() +}); + +export const exitNodeOrgs = sqliteTable("exitNodeOrgs", { + exitNodeId: integer("exitNodeId") + .notNull() + .references(() => exitNodes.exitNodeId, { onDelete: "cascade" }), + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }) +}); + +export const remoteExitNodes = sqliteTable("remoteExitNode", { + remoteExitNodeId: text("id").primaryKey(), + secretHash: text("secretHash").notNull(), + dateCreated: text("dateCreated").notNull(), + version: text("version"), + exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { + onDelete: "cascade" + }) +}); + +export const remoteExitNodeSessions = sqliteTable("remoteExitNodeSession", { + sessionId: text("id").primaryKey(), + remoteExitNodeId: text("remoteExitNodeId") + .notNull() + .references(() => remoteExitNodes.remoteExitNodeId, { + onDelete: "cascade" + }), + expiresAt: integer("expiresAt").notNull() +}); + +export const loginPage = sqliteTable("loginPage", { + loginPageId: integer("loginPageId").primaryKey({ autoIncrement: true }), + subdomain: text("subdomain"), + fullDomain: text("fullDomain"), + exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { + onDelete: "set null" + }), + domainId: text("domainId").references(() => domains.domainId, { + onDelete: "set null" + }) +}); + +export const loginPageOrg = sqliteTable("loginPageOrg", { + loginPageId: integer("loginPageId") + .notNull() + .references(() => loginPage.loginPageId, { onDelete: "cascade" }), + orgId: text("orgId") + .notNull() + .references(() => orgs.orgId, { onDelete: "cascade" }) +}); + +export const sessionTransferToken = sqliteTable("sessionTransferToken", { + token: text("token").primaryKey(), + sessionId: text("sessionId") + .notNull() + .references(() => sessions.sessionId, { + onDelete: "cascade" + }), + encryptedSession: text("encryptedSession").notNull(), + expiresAt: integer("expiresAt").notNull() +}); + +export type Limit = InferSelectModel; +export type Account = InferSelectModel; +export type Certificate = InferSelectModel; +export type DnsChallenge = InferSelectModel; +export type Customer = InferSelectModel; +export type Subscription = InferSelectModel; +export type SubscriptionItem = InferSelectModel; +export type Usage = InferSelectModel; +export type UsageLimit = InferSelectModel; +export type AccountDomain = InferSelectModel; +export type UsageNotification = InferSelectModel; +export type RemoteExitNode = InferSelectModel; +export type RemoteExitNodeSession = InferSelectModel< + typeof remoteExitNodeSessions +>; +export type ExitNodeOrg = InferSelectModel; +export type LoginPage = InferSelectModel; diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index e38d6b67..62fca8b4 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -140,6 +140,27 @@ export const targets = sqliteTable("targets", { rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix }); +export const targetHealthCheck = sqliteTable("targetHealthCheck", { + targetHealthCheckId: integer("targetHealthCheckId").primaryKey({ autoIncrement: true }), + targetId: integer("targetId") + .notNull() + .references(() => targets.targetId, { onDelete: "cascade" }), + hcEnabled: integer("hcEnabled", { mode: "boolean" }).notNull().default(false), + hcPath: text("hcPath"), + hcScheme: text("hcScheme"), + hcMode: text("hcMode").default("http"), + hcHostname: text("hcHostname"), + hcPort: integer("hcPort"), + hcInterval: integer("hcInterval").default(30), // in seconds + hcUnhealthyInterval: integer("hcUnhealthyInterval").default(30), // in seconds + hcTimeout: integer("hcTimeout").default(5), // in seconds + hcHeaders: text("hcHeaders"), + hcFollowRedirects: integer("hcFollowRedirects", { mode: "boolean" }).default(true), + hcMethod: text("hcMethod").default("GET"), + hcStatus: integer("hcStatus"), // http code + hcHealth: text("hcHealth").default("unknown") // "unknown", "healthy", "unhealthy" +}); + export const exitNodes = sqliteTable("exitNodes", { exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }), name: text("name").notNull(), @@ -458,18 +479,6 @@ export const userResources = sqliteTable("userResources", { .references(() => resources.resourceId, { onDelete: "cascade" }) }); -export const limitsTable = sqliteTable("limits", { - limitId: integer("limitId").primaryKey({ autoIncrement: true }), - orgId: text("orgId") - .references(() => orgs.orgId, { - onDelete: "cascade" - }) - .notNull(), - name: text("name").notNull(), - value: integer("value").notNull(), - description: text("description") -}); - export const userInvites = sqliteTable("userInvites", { inviteId: text("inviteId").primaryKey(), orgId: text("orgId") @@ -714,7 +723,6 @@ export type RoleSite = InferSelectModel; export type UserSite = InferSelectModel; export type RoleResource = InferSelectModel; export type UserResource = InferSelectModel; -export type Limit = InferSelectModel; export type UserInvite = InferSelectModel; export type UserOrg = InferSelectModel; export type ResourceSession = InferSelectModel; @@ -739,3 +747,4 @@ export type SiteResource = InferSelectModel; export type OrgDomains = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; +export type TargetHealthCheck = InferSelectModel; \ No newline at end of file diff --git a/server/emails/sendEmail.ts b/server/emails/sendEmail.ts index 9b99d18e..24fb4fbd 100644 --- a/server/emails/sendEmail.ts +++ b/server/emails/sendEmail.ts @@ -11,7 +11,7 @@ export async function sendEmail( from: string | undefined; to: string | undefined; subject: string; - }, + } ) { if (!emailClient) { logger.warn("Email client not configured, skipping email send"); @@ -25,16 +25,16 @@ export async function sendEmail( const emailHtml = await render(template); - const appName = "Pangolin"; + const appName = config.getRawPrivateConfig().branding?.app_name || "Pangolin"; await emailClient.sendMail({ from: { name: opts.name || appName, - address: opts.from, + address: opts.from }, to: opts.to, subject: opts.subject, - html: emailHtml, + html: emailHtml }); } diff --git a/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx b/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx new file mode 100644 index 00000000..c66265e5 --- /dev/null +++ b/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx @@ -0,0 +1,82 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import React from "react"; +import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; +import { themeColors } from "./lib/theme"; +import { + EmailContainer, + EmailFooter, + EmailGreeting, + EmailHeading, + EmailLetterHead, + EmailSignature, + EmailText +} from "./components/Email"; + +interface Props { + email: string; + limitName: string; + currentUsage: number; + usageLimit: number; + billingLink: string; // Link to billing page +} + +export const NotifyUsageLimitApproaching = ({ email, limitName, currentUsage, usageLimit, billingLink }: Props) => { + const previewText = `Your usage for ${limitName} is approaching the limit.`; + const usagePercentage = Math.round((currentUsage / usageLimit) * 100); + + return ( + + + {previewText} + + + + + + Usage Limit Warning + + Hi there, + + + We wanted to let you know that your usage for {limitName} is approaching your plan limit. + + + + Current Usage: {currentUsage} of {usageLimit} ({usagePercentage}%) + + + + Once you reach your limit, some functionality may be restricted or your sites may disconnect until you upgrade your plan or your usage resets. + + + + To avoid any interruption to your service, we recommend upgrading your plan or monitoring your usage closely. You can upgrade your plan here. + + + + If you have any questions or need assistance, please don't hesitate to reach out to our support team. + + + + + + + + + + ); +}; + +export default NotifyUsageLimitApproaching; diff --git a/server/emails/templates/PrivateNotifyUsageLimitReached.tsx b/server/emails/templates/PrivateNotifyUsageLimitReached.tsx new file mode 100644 index 00000000..c4eac322 --- /dev/null +++ b/server/emails/templates/PrivateNotifyUsageLimitReached.tsx @@ -0,0 +1,84 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import React from "react"; +import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; +import { themeColors } from "./lib/theme"; +import { + EmailContainer, + EmailFooter, + EmailGreeting, + EmailHeading, + EmailLetterHead, + EmailSignature, + EmailText +} from "./components/Email"; + +interface Props { + email: string; + limitName: string; + currentUsage: number; + usageLimit: number; + billingLink: string; // Link to billing page +} + +export const NotifyUsageLimitReached = ({ email, limitName, currentUsage, usageLimit, billingLink }: Props) => { + const previewText = `You've reached your ${limitName} usage limit - Action required`; + const usagePercentage = Math.round((currentUsage / usageLimit) * 100); + + return ( + + + {previewText} + + + + + + Usage Limit Reached - Action Required + + Hi there, + + + You have reached your usage limit for {limitName}. + + + + Current Usage: {currentUsage} of {usageLimit} ({usagePercentage}%) + + + + Important: Your functionality may now be restricted and your sites may disconnect until you either upgrade your plan or your usage resets. To prevent any service interruption, immediate action is recommended. + + + + What you can do: +
Upgrade your plan immediately to restore full functionality +
• Monitor your usage to stay within limits in the future +
+ + + If you have any questions or need immediate assistance, please contact our support team right away. + + + + + +
+ +
+ + ); +}; + +export default NotifyUsageLimitReached; diff --git a/server/emails/templates/components/Email.tsx b/server/emails/templates/components/Email.tsx index ef5c37f8..d064c44f 100644 --- a/server/emails/templates/components/Email.tsx +++ b/server/emails/templates/components/Email.tsx @@ -17,7 +17,7 @@ export function EmailLetterHead() {
Fossorial Best regards,
- The Fossorial Team + The Pangolin Team

); diff --git a/server/hybridServer.ts b/server/hybridServer.ts index bb26489d..7e9ce095 100644 --- a/server/hybridServer.ts +++ b/server/hybridServer.ts @@ -3,7 +3,7 @@ import config from "@server/lib/config"; import { createWebSocketClient } from "./routers/ws/client"; import { addPeer, deletePeer } from "./routers/gerbil/peers"; import { db, exitNodes } from "./db"; -import { TraefikConfigManager } from "./lib/traefikConfig"; +import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager"; import { tokenManager } from "./lib/tokenManager"; import { APP_VERSION } from "./lib/consts"; import axios from "axios"; diff --git a/server/index.ts b/server/index.ts index 746de7b9..2497e301 100644 --- a/server/index.ts +++ b/server/index.ts @@ -5,13 +5,13 @@ import { runSetupFunctions } from "./setup"; import { createApiServer } from "./apiServer"; import { createNextServer } from "./nextServer"; import { createInternalServer } from "./internalServer"; -import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "@server/db"; +import { ApiKey, ApiKeyOrg, RemoteExitNode, Session, User, UserOrg } from "@server/db"; import { createIntegrationApiServer } from "./integrationApiServer"; import { createHybridClientServer } from "./hybridServer"; import config from "@server/lib/config"; import { setHostMeta } from "@server/lib/hostMeta"; import { initTelemetryClient } from "./lib/telemetry.js"; -import { TraefikConfigManager } from "./lib/traefikConfig.js"; +import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager.js"; async function startServers() { await setHostMeta(); @@ -63,6 +63,7 @@ declare global { userOrgRoleId?: number; userOrgId?: string; userOrgIds?: string[]; + remoteExitNode?: RemoteExitNode; } } } diff --git a/server/integrationApiServer.ts b/server/integrationApiServer.ts index eefaacd8..cbbea83f 100644 --- a/server/integrationApiServer.ts +++ b/server/integrationApiServer.ts @@ -13,6 +13,10 @@ import helmet from "helmet"; import swaggerUi from "swagger-ui-express"; import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi"; import { registry } from "./openApi"; +import fs from "fs"; +import path from "path"; +import { APP_PATH } from "./lib/consts"; +import yaml from "js-yaml"; const dev = process.env.ENVIRONMENT !== "prod"; const externalPort = config.getRawConfig().server.integration_port; @@ -92,7 +96,7 @@ function getOpenApiDocumentation() { const generator = new OpenApiGeneratorV3(registry.definitions); - return generator.generateDocument({ + const generated = generator.generateDocument({ openapi: "3.0.0", info: { version: "v1", @@ -100,4 +104,12 @@ function getOpenApiDocumentation() { }, servers: [{ url: "/v1" }] }); + + // convert to yaml and save to file + const outputPath = path.join(APP_PATH, "openapi.yaml"); + const yamlOutput = yaml.dump(generated); + fs.writeFileSync(outputPath, yamlOutput, "utf8"); + logger.info(`OpenAPI documentation saved to ${outputPath}`); + + return generated; } diff --git a/server/lib/blueprints/applyBlueprint.ts b/server/lib/blueprints/applyBlueprint.ts index 47193420..6fac099a 100644 --- a/server/lib/blueprints/applyBlueprint.ts +++ b/server/lib/blueprints/applyBlueprint.ts @@ -69,9 +69,16 @@ export async function applyBlueprint( `Updating target ${target.targetId} on site ${site.sites.siteId}` ); + // see if you can find a matching target health check from the healthchecksToUpdate array + const matchingHealthcheck = + result.healthchecksToUpdate.find( + (hc) => hc.targetId === target.targetId + ); + await addProxyTargets( site.newt.newtId, [target], + matchingHealthcheck ? [matchingHealthcheck] : [], result.proxyResource.protocol, result.proxyResource.proxyPort ); diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index 9b349281..e6525191 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -8,6 +8,8 @@ import { roleResources, roles, Target, + TargetHealthCheck, + targetHealthCheck, Transaction, userOrgs, userResources, @@ -22,6 +24,7 @@ import { TargetData } from "./types"; import logger from "@server/logger"; +import { createCertificate } from "@server/routers/private/certificates/createCertificate"; import { pickPort } from "@server/routers/target/helpers"; import { resourcePassword } from "@server/db"; import { hashPassword } from "@server/auth/password"; @@ -30,6 +33,7 @@ import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators"; export type ProxyResourcesResults = { proxyResource: Resource; targetsToUpdate: Target[]; + healthchecksToUpdate: TargetHealthCheck[]; }[]; export async function updateProxyResources( @@ -43,7 +47,8 @@ export async function updateProxyResources( for (const [resourceNiceId, resourceData] of Object.entries( config["proxy-resources"] )) { - const targetsToUpdate: Target[] = []; + let targetsToUpdate: Target[] = []; + let healthchecksToUpdate: TargetHealthCheck[] = []; let resource: Resource; async function createTarget( // reusable function to create a target @@ -114,6 +119,33 @@ export async function updateProxyResources( .returning(); targetsToUpdate.push(newTarget); + + const healthcheckData = targetData.healthcheck; + + const hcHeaders = healthcheckData?.headers ? JSON.stringify(healthcheckData.headers) : null; + + const [newHealthcheck] = await trx + .insert(targetHealthCheck) + .values({ + targetId: newTarget.targetId, + hcEnabled: healthcheckData?.enabled || false, + hcPath: healthcheckData?.path, + hcScheme: healthcheckData?.scheme, + hcMode: healthcheckData?.mode, + hcHostname: healthcheckData?.hostname, + hcPort: healthcheckData?.port, + hcInterval: healthcheckData?.interval, + hcUnhealthyInterval: healthcheckData?.unhealthyInterval, + hcTimeout: healthcheckData?.timeout, + hcHeaders: hcHeaders, + hcFollowRedirects: healthcheckData?.followRedirects, + hcMethod: healthcheckData?.method, + hcStatus: healthcheckData?.status, + hcHealth: "unknown" + }) + .returning(); + + healthchecksToUpdate.push(newHealthcheck); } // Find existing resource by niceId and orgId @@ -360,6 +392,64 @@ export async function updateProxyResources( targetsToUpdate.push(finalUpdatedTarget); } + + const healthcheckData = targetData.healthcheck; + + const [oldHealthcheck] = await trx + .select() + .from(targetHealthCheck) + .where( + eq( + targetHealthCheck.targetId, + existingTarget.targetId + ) + ) + .limit(1); + + const hcHeaders = healthcheckData?.headers ? JSON.stringify(healthcheckData.headers) : null; + + const [newHealthcheck] = await trx + .update(targetHealthCheck) + .set({ + hcEnabled: healthcheckData?.enabled || false, + hcPath: healthcheckData?.path, + hcScheme: healthcheckData?.scheme, + hcMode: healthcheckData?.mode, + hcHostname: healthcheckData?.hostname, + hcPort: healthcheckData?.port, + hcInterval: healthcheckData?.interval, + hcUnhealthyInterval: + healthcheckData?.unhealthyInterval, + hcTimeout: healthcheckData?.timeout, + hcHeaders: hcHeaders, + hcFollowRedirects: healthcheckData?.followRedirects, + hcMethod: healthcheckData?.method, + hcStatus: healthcheckData?.status + }) + .where( + eq( + targetHealthCheck.targetId, + existingTarget.targetId + ) + ) + .returning(); + + if ( + checkIfHealthcheckChanged( + oldHealthcheck, + newHealthcheck + ) + ) { + healthchecksToUpdate.push(newHealthcheck); + // if the target is not already in the targetsToUpdate array, add it + if ( + !targetsToUpdate.find( + (t) => t.targetId === updatedTarget.targetId + ) + ) { + targetsToUpdate.push(updatedTarget); + } + } } else { await createTarget(existingResource.resourceId, targetData); } @@ -573,7 +663,8 @@ export async function updateProxyResources( results.push({ proxyResource: resource, - targetsToUpdate + targetsToUpdate, + healthchecksToUpdate }); } @@ -783,6 +874,36 @@ async function syncWhitelistUsers( } } +function checkIfHealthcheckChanged( + existing: TargetHealthCheck | undefined, + incoming: TargetHealthCheck | undefined +) { + if (!existing && incoming) return true; + if (existing && !incoming) return true; + if (!existing || !incoming) return false; + + if (existing.hcEnabled !== incoming.hcEnabled) return true; + if (existing.hcPath !== incoming.hcPath) return true; + if (existing.hcScheme !== incoming.hcScheme) return true; + if (existing.hcMode !== incoming.hcMode) return true; + if (existing.hcHostname !== incoming.hcHostname) return true; + if (existing.hcPort !== incoming.hcPort) return true; + if (existing.hcInterval !== incoming.hcInterval) return true; + if (existing.hcUnhealthyInterval !== incoming.hcUnhealthyInterval) + return true; + if (existing.hcTimeout !== incoming.hcTimeout) return true; + if (existing.hcFollowRedirects !== incoming.hcFollowRedirects) return true; + if (existing.hcMethod !== incoming.hcMethod) return true; + if (existing.hcStatus !== incoming.hcStatus) return true; + if ( + JSON.stringify(existing.hcHeaders) !== + JSON.stringify(incoming.hcHeaders) + ) + return true; + + return false; +} + function checkIfTargetChanged( existing: Target | undefined, incoming: Target | undefined @@ -832,6 +953,8 @@ async function getDomain( ); } + await createCertificate(domain.domainId, fullDomain, trx); + return domain; } diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index dda672a6..54105dde 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -5,6 +5,22 @@ export const SiteSchema = z.object({ "docker-socket-enabled": z.boolean().optional().default(true) }); +export const TargetHealthCheckSchema = z.object({ + hostname: z.string(), + port: z.number().int().min(1).max(65535), + enabled: z.boolean().optional().default(true), + path: z.string().optional(), + scheme: z.string().optional(), + mode: z.string().default("http"), + interval: z.number().int().default(30), + unhealthyInterval: z.number().int().default(30), + timeout: z.number().int().default(5), + headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null), + followRedirects: z.boolean().default(true), + method: z.string().default("GET"), + status: z.number().int().optional() +}); + // Schema for individual target within a resource export const TargetSchema = z.object({ site: z.string().optional(), @@ -15,6 +31,7 @@ export const TargetSchema = z.object({ "internal-port": z.number().int().min(1).max(65535).optional(), path: z.string().optional(), "path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(), + healthcheck: TargetHealthCheckSchema.optional(), rewritePath: z.string().optional(), "rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() }); diff --git a/server/lib/colorsSchema.ts b/server/lib/colorsSchema.ts new file mode 100644 index 00000000..0aeb65c3 --- /dev/null +++ b/server/lib/colorsSchema.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; + +export const colorsSchema = z.object({ + background: z.string().optional(), + foreground: z.string().optional(), + card: z.string().optional(), + "card-foreground": z.string().optional(), + popover: z.string().optional(), + "popover-foreground": z.string().optional(), + primary: z.string().optional(), + "primary-foreground": z.string().optional(), + secondary: z.string().optional(), + "secondary-foreground": z.string().optional(), + muted: z.string().optional(), + "muted-foreground": z.string().optional(), + accent: z.string().optional(), + "accent-foreground": z.string().optional(), + destructive: z.string().optional(), + "destructive-foreground": z.string().optional(), + border: z.string().optional(), + input: z.string().optional(), + ring: z.string().optional(), + radius: z.string().optional(), + "chart-1": z.string().optional(), + "chart-2": z.string().optional(), + "chart-3": z.string().optional(), + "chart-4": z.string().optional(), + "chart-5": z.string().optional() +}); diff --git a/server/lib/config.ts b/server/lib/config.ts index 667df744..8b084e62 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -6,9 +6,16 @@ import { eq } from "drizzle-orm"; import { license } from "@server/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; import { fromError } from "zod-validation-error"; +import { + privateConfigSchema, + readPrivateConfigFile +} from "@server/lib/private/readConfigFile"; +import logger from "@server/logger"; +import { build } from "@server/build"; export class Config { private rawConfig!: z.infer; + private rawPrivateConfig!: z.infer; supporterData: SupporterKey | null = null; @@ -30,6 +37,19 @@ export class Config { throw new Error(`Invalid configuration file: ${errors}`); } + const privateEnvironment = readPrivateConfigFile(); + + const { + data: parsedPrivateConfig, + success: privateSuccess, + error: privateError + } = privateConfigSchema.safeParse(privateEnvironment); + + if (!privateSuccess) { + const errors = fromError(privateError); + throw new Error(`Invalid private configuration file: ${errors}`); + } + if ( // @ts-ignore parsedConfig.users || @@ -89,7 +109,110 @@ export class Config { ? "true" : "false"; + if (parsedPrivateConfig.branding?.colors) { + process.env.BRANDING_COLORS = JSON.stringify( + parsedPrivateConfig.branding?.colors + ); + } + + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + process.env.HIDE_SUPPORTER_KEY = parsedPrivateConfig.flags + ?.hide_supporter_key + ? "true" + : "false"; + + if (build != "oss") { + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + process.env.BRANDING_LOGO_AUTH_WIDTH = parsedPrivateConfig.branding + ?.logo?.auth_page?.width + ? parsedPrivateConfig.branding?.logo?.auth_page?.width.toString() + : undefined; + process.env.BRANDING_LOGO_AUTH_HEIGHT = parsedPrivateConfig.branding + ?.logo?.auth_page?.height + ? parsedPrivateConfig.branding?.logo?.auth_page?.height.toString() + : undefined; + + process.env.BRANDING_LOGO_NAVBAR_WIDTH = parsedPrivateConfig + .branding?.logo?.navbar?.width + ? parsedPrivateConfig.branding?.logo?.navbar?.width.toString() + : undefined; + process.env.BRANDING_LOGO_NAVBAR_HEIGHT = parsedPrivateConfig + .branding?.logo?.navbar?.height + ? parsedPrivateConfig.branding?.logo?.navbar?.height.toString() + : undefined; + + process.env.BRANDING_FAVICON_PATH = + parsedPrivateConfig.branding?.favicon_path; + + process.env.BRANDING_APP_NAME = + parsedPrivateConfig.branding?.app_name || "Pangolin"; + + if (parsedPrivateConfig.branding?.footer) { + process.env.BRANDING_FOOTER = JSON.stringify( + parsedPrivateConfig.branding?.footer + ); + } + + process.env.LOGIN_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.title_text || ""; + process.env.LOGIN_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.subtitle_text || ""; + + process.env.SIGNUP_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.title_text || ""; + process.env.SIGNUP_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.subtitle_text || ""; + + process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = + parsedPrivateConfig.branding?.resource_auth_page + ?.hide_powered_by === true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = + parsedPrivateConfig.branding?.resource_auth_page?.show_logo === + true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page?.title_text || + ""; + process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page + ?.subtitle_text || ""; + + if (parsedPrivateConfig.branding?.background_image_path) { + process.env.BACKGROUND_IMAGE_PATH = + parsedPrivateConfig.branding?.background_image_path; + } + + if (parsedPrivateConfig.server.reo_client_id) { + process.env.REO_CLIENT_ID = + parsedPrivateConfig.server.reo_client_id; + } + } + + if (parsedConfig.server.maxmind_db_path) { + process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path; + } + this.rawConfig = parsedConfig; + this.rawPrivateConfig = parsedPrivateConfig; } public async initServer() { @@ -107,7 +230,11 @@ export class Config { private async checkKeyStatus() { const licenseStatus = await license.check(); - if (!licenseStatus.isHostLicensed) { + if ( + !this.rawPrivateConfig.flags?.hide_supporter_key && + build != "oss" && + !licenseStatus.isHostLicensed + ) { this.checkSupporterKey(); } } @@ -116,6 +243,10 @@ export class Config { return this.rawConfig; } + public getRawPrivateConfig() { + return this.rawPrivateConfig; + } + public getNoReplyEmail(): string | undefined { return ( this.rawConfig.email?.no_reply || this.rawConfig.email?.smtp_user diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 5df517b8..544123a9 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -11,3 +11,5 @@ export const APP_PATH = path.join("config"); export const configFilePath1 = path.join(APP_PATH, "config.yml"); export const configFilePath2 = path.join(APP_PATH, "config.yaml"); + +export const privateConfigFilePath1 = path.join(APP_PATH, "privateConfig.yml"); diff --git a/server/lib/encryption.ts b/server/lib/encryption.ts new file mode 100644 index 00000000..7959fa4b --- /dev/null +++ b/server/lib/encryption.ts @@ -0,0 +1,39 @@ +import crypto from 'crypto'; + +export function encryptData(data: string, key: Buffer): string { + const algorithm = 'aes-256-gcm'; + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(algorithm, key, iv); + + let encrypted = cipher.update(data, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + + const authTag = cipher.getAuthTag(); + + // Combine IV, auth tag, and encrypted data + return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted; +} + +// Helper function to decrypt data (you'll need this to read certificates) +export function decryptData(encryptedData: string, key: Buffer): string { + const algorithm = 'aes-256-gcm'; + const parts = encryptedData.split(':'); + + if (parts.length !== 3) { + throw new Error('Invalid encrypted data format'); + } + + const iv = Buffer.from(parts[0], 'hex'); + const authTag = Buffer.from(parts[1], 'hex'); + const encrypted = parts[2]; + + const decipher = crypto.createDecipheriv(algorithm, key, iv); + decipher.setAuthTag(authTag); + + let decrypted = decipher.update(encrypted, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + + return decrypted; +} + +// openssl rand -hex 32 > config/encryption.key \ No newline at end of file diff --git a/server/lib/exitNodeComms.ts b/server/lib/exitNodes/exitNodeComms.ts similarity index 100% rename from server/lib/exitNodeComms.ts rename to server/lib/exitNodes/exitNodeComms.ts diff --git a/server/lib/exitNodes/exitNodes.ts b/server/lib/exitNodes/exitNodes.ts index 7b571682..8372d675 100644 --- a/server/lib/exitNodes/exitNodes.ts +++ b/server/lib/exitNodes/exitNodes.ts @@ -16,7 +16,7 @@ export async function verifyExitNodeOrgAccess( return { hasAccess: true, exitNode }; } -export async function listExitNodes(orgId: string, filterOnline = false) { +export async function listExitNodes(orgId: string, filterOnline = false, noCloud = false) { // TODO: pick which nodes to send and ping better than just all of them that are not remote const allExitNodes = await db .select({ @@ -57,4 +57,9 @@ export function selectBestExitNode( export async function checkExitNodeOrg(exitNodeId: number, orgId: string) { return false; -} \ No newline at end of file +} + +export async function resolveExitNodes(hostname: string, publicKey: string) { + // OSS version: simple implementation that returns empty array + return []; +} diff --git a/server/lib/exitNodes/getCurrentExitNodeId.ts b/server/lib/exitNodes/getCurrentExitNodeId.ts new file mode 100644 index 00000000..d895ce42 --- /dev/null +++ b/server/lib/exitNodes/getCurrentExitNodeId.ts @@ -0,0 +1,33 @@ +import { eq } from "drizzle-orm"; +import { db, exitNodes } from "@server/db"; +import config from "@server/lib/config"; + +let currentExitNodeId: number; // we really only need to look this up once per instance +export async function getCurrentExitNodeId(): Promise { + if (!currentExitNodeId) { + if (config.getRawConfig().gerbil.exit_node_name) { + const exitNodeName = config.getRawConfig().gerbil.exit_node_name!; + const [exitNode] = await db + .select({ + exitNodeId: exitNodes.exitNodeId + }) + .from(exitNodes) + .where(eq(exitNodes.name, exitNodeName)); + if (exitNode) { + currentExitNodeId = exitNode.exitNodeId; + } + } else { + const [exitNode] = await db + .select({ + exitNodeId: exitNodes.exitNodeId + }) + .from(exitNodes) + .limit(1); + + if (exitNode) { + currentExitNodeId = exitNode.exitNodeId; + } + } + } + return currentExitNodeId; +} \ No newline at end of file diff --git a/server/lib/exitNodes/index.ts b/server/lib/exitNodes/index.ts index 8889bc35..dda94368 100644 --- a/server/lib/exitNodes/index.ts +++ b/server/lib/exitNodes/index.ts @@ -1,2 +1,33 @@ -export * from "./exitNodes"; -export * from "./shared"; \ No newline at end of file +import { build } from "@server/build"; + +// Import both modules +import * as exitNodesModule from "./exitNodes"; +import * as privateExitNodesModule from "./privateExitNodes"; + +// Conditionally export exit nodes implementation based on build type +const exitNodesImplementation = build === "oss" ? exitNodesModule : privateExitNodesModule; + +// Re-export all items from the selected implementation +export const { + verifyExitNodeOrgAccess, + listExitNodes, + selectBestExitNode, + checkExitNodeOrg, + resolveExitNodes +} = exitNodesImplementation; + +// Import communications modules +import * as exitNodeCommsModule from "./exitNodeComms"; +import * as privateExitNodeCommsModule from "./privateExitNodeComms"; + +// Conditionally export communications implementation based on build type +const exitNodeCommsImplementation = build === "oss" ? exitNodeCommsModule : privateExitNodeCommsModule; + +// Re-export communications functions from the selected implementation +export const { + sendToExitNode +} = exitNodeCommsImplementation; + +// Re-export shared modules +export * from "./subnet"; +export * from "./getCurrentExitNodeId"; \ No newline at end of file diff --git a/server/lib/exitNodes/privateExitNodeComms.ts b/server/lib/exitNodes/privateExitNodeComms.ts new file mode 100644 index 00000000..163a962f --- /dev/null +++ b/server/lib/exitNodes/privateExitNodeComms.ts @@ -0,0 +1,145 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import axios from "axios"; +import logger from "@server/logger"; +import { db, ExitNode, remoteExitNodes } from "@server/db"; +import { eq } from "drizzle-orm"; +import { sendToClient } from "../../routers/ws"; +import { config } from "../config"; + +interface ExitNodeRequest { + remoteType?: string; + localPath: string; + method?: "POST" | "DELETE" | "GET" | "PUT"; + data?: any; + queryParams?: Record; +} + +/** + * Sends a request to an exit node, handling both remote and local exit nodes + * @param exitNode The exit node to send the request to + * @param request The request configuration + * @returns Promise Response data for local nodes, undefined for remote nodes + */ +export async function sendToExitNode( + exitNode: ExitNode, + request: ExitNodeRequest +): Promise { + if (exitNode.type === "remoteExitNode" && request.remoteType) { + const [remoteExitNode] = await db + .select() + .from(remoteExitNodes) + .where(eq(remoteExitNodes.exitNodeId, exitNode.exitNodeId)) + .limit(1); + + if (!remoteExitNode) { + throw new Error( + `Remote exit node with ID ${exitNode.exitNodeId} not found` + ); + } + + return sendToClient(remoteExitNode.remoteExitNodeId, { + type: request.remoteType, + data: request.data + }); + } else { + let hostname = exitNode.reachableAt; + + logger.debug(`Exit node details:`, { + type: exitNode.type, + name: exitNode.name, + reachableAt: exitNode.reachableAt, + }); + + logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); + + if (exitNode.name == config.getRawConfig().gerbil.exit_node_name) { + hostname = config.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; + } + + if (!hostname) { + throw new Error( + `Exit node with ID ${exitNode.exitNodeId} is not reachable` + ); + } + + logger.debug(`Sending request to exit node at ${hostname}`, { + type: request.remoteType, + data: request.data + }); + + // Handle local exit node with HTTP API + const method = request.method || "POST"; + let url = `${hostname}${request.localPath}`; + + // Add query parameters if provided + if (request.queryParams) { + const params = new URLSearchParams(request.queryParams); + url += `?${params.toString()}`; + } + + try { + let response; + + switch (method) { + case "POST": + response = await axios.post(url, request.data, { + headers: { + "Content-Type": "application/json" + }, + timeout: 8000 + }); + break; + case "DELETE": + response = await axios.delete(url, { + timeout: 8000 + }); + break; + case "GET": + response = await axios.get(url, { + timeout: 8000 + }); + break; + case "PUT": + response = await axios.put(url, request.data, { + headers: { + "Content-Type": "application/json" + }, + timeout: 8000 + }); + break; + default: + throw new Error(`Unsupported HTTP method: ${method}`); + } + + logger.debug(`Exit node request successful:`, { + method, + url, + status: response.data.status + }); + + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error( + `Error making ${method} request (can Pangolin see Gerbil HTTP API?) for exit node at ${hostname} (status: ${error.response?.status}): ${error.message}` + ); + } else { + logger.error( + `Error making ${method} request for exit node at ${hostname}: ${error}` + ); + } + } + } +} diff --git a/server/lib/exitNodes/privateExitNodes.ts b/server/lib/exitNodes/privateExitNodes.ts new file mode 100644 index 00000000..ea83fe9d --- /dev/null +++ b/server/lib/exitNodes/privateExitNodes.ts @@ -0,0 +1,379 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + db, + exitNodes, + exitNodeOrgs, + resources, + targets, + sites, + targetHealthCheck +} from "@server/db"; +import logger from "@server/logger"; +import { ExitNodePingResult } from "@server/routers/newt"; +import { eq, and, or, ne, isNull } from "drizzle-orm"; +import axios from "axios"; +import config from "../config"; + +/** + * Checks if an exit node is actually online by making HTTP requests to its endpoint/ping + * Makes up to 3 attempts in parallel with small delays, returns as soon as one succeeds + */ +async function checkExitNodeOnlineStatus( + endpoint: string | undefined +): Promise { + if (!endpoint || endpoint == "") { + // the endpoint can start out as a empty string + return false; + } + + const maxAttempts = 3; + const timeoutMs = 5000; // 5 second timeout per request + const delayBetweenAttempts = 100; // 100ms delay between starting each attempt + + // Create promises for all attempts with staggered delays + const attemptPromises = Array.from({ length: maxAttempts }, async (_, index) => { + const attemptNumber = index + 1; + + // Add delay before each attempt (except the first) + if (index > 0) { + await new Promise((resolve) => setTimeout(resolve, delayBetweenAttempts * index)); + } + + try { + const response = await axios.get(`http://${endpoint}/ping`, { + timeout: timeoutMs, + validateStatus: (status) => status === 200 + }); + + if (response.status === 200) { + logger.debug( + `Exit node ${endpoint} is online (attempt ${attemptNumber}/${maxAttempts})` + ); + return { success: true, attemptNumber }; + } + return { success: false, attemptNumber, error: 'Non-200 status' }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : "Unknown error"; + logger.debug( + `Exit node ${endpoint} ping failed (attempt ${attemptNumber}/${maxAttempts}): ${errorMessage}` + ); + return { success: false, attemptNumber, error: errorMessage }; + } + }); + + try { + // Wait for the first successful response or all to fail + const results = await Promise.allSettled(attemptPromises); + + // Check if any attempt succeeded + for (const result of results) { + if (result.status === 'fulfilled' && result.value.success) { + return true; + } + } + + // All attempts failed + logger.warn( + `Exit node ${endpoint} is offline after ${maxAttempts} parallel attempts` + ); + return false; + } catch (error) { + logger.warn( + `Unexpected error checking exit node ${endpoint}: ${error instanceof Error ? error.message : "Unknown error"}` + ); + return false; + } +} + +export async function verifyExitNodeOrgAccess( + exitNodeId: number, + orgId: string +) { + const [result] = await db + .select({ + exitNode: exitNodes, + exitNodeOrgId: exitNodeOrgs.exitNodeId + }) + .from(exitNodes) + .leftJoin( + exitNodeOrgs, + and( + eq(exitNodeOrgs.exitNodeId, exitNodes.exitNodeId), + eq(exitNodeOrgs.orgId, orgId) + ) + ) + .where(eq(exitNodes.exitNodeId, exitNodeId)); + + if (!result) { + return { hasAccess: false, exitNode: null }; + } + + const { exitNode } = result; + + // If the exit node is type "gerbil", access is allowed + if (exitNode.type === "gerbil") { + return { hasAccess: true, exitNode }; + } + + // If the exit node is type "remoteExitNode", check if it has org access + if (exitNode.type === "remoteExitNode") { + return { hasAccess: !!result.exitNodeOrgId, exitNode }; + } + + // For any other type, deny access + return { hasAccess: false, exitNode }; +} + +export async function listExitNodes(orgId: string, filterOnline = false, noCloud = false) { + const allExitNodes = await db + .select({ + exitNodeId: exitNodes.exitNodeId, + name: exitNodes.name, + address: exitNodes.address, + endpoint: exitNodes.endpoint, + publicKey: exitNodes.publicKey, + listenPort: exitNodes.listenPort, + reachableAt: exitNodes.reachableAt, + maxConnections: exitNodes.maxConnections, + online: exitNodes.online, + lastPing: exitNodes.lastPing, + type: exitNodes.type, + orgId: exitNodeOrgs.orgId, + region: exitNodes.region + }) + .from(exitNodes) + .leftJoin( + exitNodeOrgs, + eq(exitNodes.exitNodeId, exitNodeOrgs.exitNodeId) + ) + .where( + or( + // Include all exit nodes that are NOT of type remoteExitNode + and( + eq(exitNodes.type, "gerbil"), + or( + // only choose nodes that are in the same region + eq(exitNodes.region, config.getRawPrivateConfig().app.region), + isNull(exitNodes.region) // or for enterprise where region is not set + ) + ), + // Include remoteExitNode types where the orgId matches the newt's organization + and( + eq(exitNodes.type, "remoteExitNode"), + eq(exitNodeOrgs.orgId, orgId) + ) + ) + ); + + // Filter the nodes. If there are NO remoteExitNodes then do nothing. If there are then remove all of the non-remoteExitNodes + if (allExitNodes.length === 0) { + logger.warn("No exit nodes found for ping request!"); + return []; + } + + // Enhanced online checking: consider node offline if either DB says offline OR HTTP ping fails + const nodesWithRealOnlineStatus = await Promise.all( + allExitNodes.map(async (node) => { + // If database says it's online, verify with HTTP ping + let online: boolean; + if (filterOnline && node.type == "remoteExitNode") { + try { + const isActuallyOnline = await checkExitNodeOnlineStatus( + node.endpoint + ); + + // set the item in the database if it is offline + if (isActuallyOnline != node.online) { + await db + .update(exitNodes) + .set({ online: isActuallyOnline }) + .where(eq(exitNodes.exitNodeId, node.exitNodeId)); + } + online = isActuallyOnline; + } catch (error) { + logger.warn( + `Failed to check online status for exit node ${node.name} (${node.endpoint}): ${error instanceof Error ? error.message : "Unknown error"}` + ); + online = false; + } + } else { + online = node.online; + } + + return { + ...node, + online + }; + }) + ); + + const remoteExitNodes = nodesWithRealOnlineStatus.filter( + (node) => + node.type === "remoteExitNode" && (!filterOnline || node.online) + ); + const gerbilExitNodes = nodesWithRealOnlineStatus.filter( + (node) => node.type === "gerbil" && (!filterOnline || node.online) && !noCloud + ); + + // THIS PROVIDES THE FALL + const exitNodesList = + remoteExitNodes.length > 0 ? remoteExitNodes : gerbilExitNodes; + + return exitNodesList; +} + +/** + * Selects the most suitable exit node from a list of ping results. + * + * The selection algorithm follows these steps: + * + * 1. **Filter Invalid Nodes**: Excludes nodes with errors or zero weight. + * + * 2. **Sort by Latency**: Sorts valid nodes in ascending order of latency. + * + * 3. **Preferred Selection**: + * - If the lowest-latency node has sufficient capacity (≥10% weight), + * check if a previously connected node is also acceptable. + * - The previously connected node is preferred if its latency is within + * 30ms or 15% of the best node’s latency. + * + * 4. **Fallback to Next Best**: + * - If the lowest-latency node is under capacity, find the next node + * with acceptable capacity. + * + * 5. **Final Fallback**: + * - If no nodes meet the capacity threshold, fall back to the node + * with the highest weight (i.e., most available capacity). + * + */ +export function selectBestExitNode( + pingResults: ExitNodePingResult[] +): ExitNodePingResult | null { + const MIN_CAPACITY_THRESHOLD = 0.1; + const LATENCY_TOLERANCE_MS = 30; + const LATENCY_TOLERANCE_PERCENT = 0.15; + + // Filter out invalid nodes + const validNodes = pingResults.filter((n) => !n.error && n.weight > 0); + + if (validNodes.length === 0) { + logger.error("No valid exit nodes available"); + return null; + } + + // Sort by latency (ascending) + const sortedNodes = validNodes + .slice() + .sort((a, b) => a.latencyMs - b.latencyMs); + const lowestLatencyNode = sortedNodes[0]; + + logger.debug( + `Lowest latency node: ${lowestLatencyNode.exitNodeName} (${lowestLatencyNode.latencyMs} ms, weight=${lowestLatencyNode.weight.toFixed(2)})` + ); + + // If lowest latency node has enough capacity, check if previously connected node is acceptable + if (lowestLatencyNode.weight >= MIN_CAPACITY_THRESHOLD) { + const previouslyConnectedNode = sortedNodes.find( + (n) => + n.wasPreviouslyConnected && n.weight >= MIN_CAPACITY_THRESHOLD + ); + + if (previouslyConnectedNode) { + const latencyDiff = + previouslyConnectedNode.latencyMs - lowestLatencyNode.latencyMs; + const percentDiff = latencyDiff / lowestLatencyNode.latencyMs; + + if ( + latencyDiff <= LATENCY_TOLERANCE_MS || + percentDiff <= LATENCY_TOLERANCE_PERCENT + ) { + logger.info( + `Sticking with previously connected node: ${previouslyConnectedNode.exitNodeName} ` + + `(${previouslyConnectedNode.latencyMs} ms), latency diff = ${latencyDiff.toFixed(1)}ms ` + + `/ ${(percentDiff * 100).toFixed(1)}%.` + ); + return previouslyConnectedNode; + } + } + + return lowestLatencyNode; + } + + // Otherwise, find the next node (after the lowest) that has enough capacity + for (let i = 1; i < sortedNodes.length; i++) { + const node = sortedNodes[i]; + if (node.weight >= MIN_CAPACITY_THRESHOLD) { + logger.info( + `Lowest latency node under capacity. Using next best: ${node.exitNodeName} ` + + `(${node.latencyMs} ms, weight=${node.weight.toFixed(2)})` + ); + return node; + } + } + + // Fallback: pick the highest weight node + const fallbackNode = validNodes.reduce((a, b) => + a.weight > b.weight ? a : b + ); + logger.warn( + `No nodes with ≥10% weight. Falling back to highest capacity node: ${fallbackNode.exitNodeName}` + ); + return fallbackNode; +} + +export async function checkExitNodeOrg(exitNodeId: number, orgId: string) { + const [exitNodeOrg] = await db + .select() + .from(exitNodeOrgs) + .where( + and( + eq(exitNodeOrgs.exitNodeId, exitNodeId), + eq(exitNodeOrgs.orgId, orgId) + ) + ) + .limit(1); + + if (!exitNodeOrg) { + return true; + } + + return false; +} + +export async function resolveExitNodes(hostname: string, publicKey: string) { + const resourceExitNodes = await db + .select({ + endpoint: exitNodes.endpoint, + publicKey: exitNodes.publicKey, + orgId: resources.orgId + }) + .from(resources) + .innerJoin(targets, eq(resources.resourceId, targets.resourceId)) + .leftJoin( + targetHealthCheck, + eq(targetHealthCheck.targetId, targets.targetId) + ) + .innerJoin(sites, eq(targets.siteId, sites.siteId)) + .innerJoin(exitNodes, eq(sites.exitNodeId, exitNodes.exitNodeId)) + .where( + and( + eq(resources.fullDomain, hostname), + ne(exitNodes.publicKey, publicKey), + ne(targetHealthCheck.hcHealth, "unhealthy") + ) + ); + + return resourceExitNodes; +} diff --git a/server/lib/exitNodes/shared.ts b/server/lib/exitNodes/subnet.ts similarity index 100% rename from server/lib/exitNodes/shared.ts rename to server/lib/exitNodes/subnet.ts diff --git a/server/lib/geoip.ts b/server/lib/geoip.ts index 042e53c9..d6252360 100644 --- a/server/lib/geoip.ts +++ b/server/lib/geoip.ts @@ -1,10 +1,42 @@ +import logger from "@server/logger"; +import { maxmindLookup } from "@server/db/maxmind"; import axios from "axios"; import config from "./config"; import { tokenManager } from "./tokenManager"; -import logger from "@server/logger"; export async function getCountryCodeForIp( ip: string +): Promise { + try { + if (!maxmindLookup) { + logger.warn( + "MaxMind DB path not configured, cannot perform GeoIP lookup" + ); + return; + } + + const result = maxmindLookup.get(ip); + + if (!result || !result.country) { + return; + } + + const { country } = result; + + logger.debug( + `GeoIP lookup successful for IP ${ip}: ${country.iso_code}` + ); + + return country.iso_code; + } catch (error) { + logger.error("Error fetching config in verify session:", error); + } + + return; +} + +export async function remoteGetCountryCodeForIp( + ip: string ): Promise { try { const response = await axios.get( diff --git a/server/lib/idp/generateRedirectUrl.ts b/server/lib/idp/generateRedirectUrl.ts index 4eea973e..077ac6f6 100644 --- a/server/lib/idp/generateRedirectUrl.ts +++ b/server/lib/idp/generateRedirectUrl.ts @@ -1,8 +1,48 @@ +import { db, loginPage, loginPageOrg } from "@server/db"; import config from "@server/lib/config"; +import { eq } from "drizzle-orm"; + +export async function generateOidcRedirectUrl( + idpId: number, + orgId?: string, + loginPageId?: number +): Promise { + let baseUrl: string | undefined; + + const secure = config.getRawConfig().app.dashboard_url?.startsWith("https"); + const method = secure ? "https" : "http"; + + if (loginPageId) { + const [res] = await db + .select() + .from(loginPage) + .where(eq(loginPage.loginPageId, loginPageId)) + .limit(1); + + if (res && res.fullDomain) { + baseUrl = `${method}://${res.fullDomain}`; + } + } else if (orgId) { + const [res] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)) + .innerJoin( + loginPage, + eq(loginPage.loginPageId, loginPageOrg.loginPageId) + ) + .limit(1); + + if (res?.loginPage && res.loginPage.domainId && res.loginPage.fullDomain) { + baseUrl = `${method}://${res.loginPage.fullDomain}`; + } + } + + if (!baseUrl) { + baseUrl = config.getRawConfig().app.dashboard_url!; + } -export function generateOidcRedirectUrl(idpId: number) { - const dashboardUrl = config.getRawConfig().app.dashboard_url; const redirectPath = `/auth/idp/${idpId}/oidc/callback`; - const redirectUrl = new URL(redirectPath, dashboardUrl).toString(); + const redirectUrl = new URL(redirectPath, baseUrl!).toString(); return redirectUrl; } diff --git a/server/lib/index.ts b/server/lib/index.ts deleted file mode 100644 index db1a73da..00000000 --- a/server/lib/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./response"; -export { tokenManager, TokenManager } from "./tokenManager"; -export * from "./geoip"; diff --git a/server/lib/private/billing/features.ts b/server/lib/private/billing/features.ts new file mode 100644 index 00000000..316d8e86 --- /dev/null +++ b/server/lib/private/billing/features.ts @@ -0,0 +1,85 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; + +export enum FeatureId { + SITE_UPTIME = "siteUptime", + USERS = "users", + EGRESS_DATA_MB = "egressDataMb", + DOMAINS = "domains", + REMOTE_EXIT_NODES = "remoteExitNodes" +} + +export const FeatureMeterIds: Record = { + [FeatureId.SITE_UPTIME]: "mtr_61Srrej5wUJuiTWgo41D3Ee2Ir7WmDLU", + [FeatureId.USERS]: "mtr_61SrreISyIWpwUNGR41D3Ee2Ir7WmQro", + [FeatureId.EGRESS_DATA_MB]: "mtr_61Srreh9eWrExDSCe41D3Ee2Ir7Wm5YW", + [FeatureId.DOMAINS]: "mtr_61Ss9nIKDNMw0LDRU41D3Ee2Ir7WmRPU", + [FeatureId.REMOTE_EXIT_NODES]: "mtr_61T86UXnfxTVXy9sD41D3Ee2Ir7WmFTE" +}; + +export const FeatureMeterIdsSandbox: Record = { + [FeatureId.SITE_UPTIME]: "mtr_test_61Snh3cees4w60gv841DCpkOb237BDEu", + [FeatureId.USERS]: "mtr_test_61Sn5fLtq1gSfRkyA41DCpkOb237B6au", + [FeatureId.EGRESS_DATA_MB]: "mtr_test_61Snh2a2m6qome5Kv41DCpkOb237B3dQ", + [FeatureId.DOMAINS]: "mtr_test_61SsA8qrdAlgPpFRQ41DCpkOb237BGts", + [FeatureId.REMOTE_EXIT_NODES]: "mtr_test_61T86Vqmwa3D9ra3341DCpkOb237B94K" +}; + +export function getFeatureMeterId(featureId: FeatureId): string { + if (process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") { + return FeatureMeterIds[featureId]; + } else { + return FeatureMeterIdsSandbox[featureId]; + } +} + +export function getFeatureIdByMetricId(metricId: string): FeatureId | undefined { + return (Object.entries(FeatureMeterIds) as [FeatureId, string][]) + .find(([_, v]) => v === metricId)?.[0]; +} + +export type FeaturePriceSet = { + [key in FeatureId]: string; +}; + +export const standardFeaturePriceSet: FeaturePriceSet = { // Free tier matches the freeLimitSet + [FeatureId.SITE_UPTIME]: "price_1RrQc4D3Ee2Ir7WmaJGZ3MtF", + [FeatureId.USERS]: "price_1RrQeJD3Ee2Ir7WmgveP3xea", + [FeatureId.EGRESS_DATA_MB]: "price_1RrQXFD3Ee2Ir7WmvGDlgxQk", + [FeatureId.DOMAINS]: "price_1Rz3tMD3Ee2Ir7Wm5qLeASzC", + [FeatureId.REMOTE_EXIT_NODES]: "price_1S46weD3Ee2Ir7Wm94KEHI4h" +}; + +export const standardFeaturePriceSetSandbox: FeaturePriceSet = { // Free tier matches the freeLimitSet + [FeatureId.SITE_UPTIME]: "price_1RefFBDCpkOb237BPrKZ8IEU", + [FeatureId.USERS]: "price_1ReNa4DCpkOb237Bc67G5muF", + [FeatureId.EGRESS_DATA_MB]: "price_1Rfp9LDCpkOb237BwuN5Oiu0", + [FeatureId.DOMAINS]: "price_1Ryi88DCpkOb237B2D6DM80b", + [FeatureId.REMOTE_EXIT_NODES]: "price_1RyiZvDCpkOb237BXpmoIYJL" +}; + +export function getStandardFeaturePriceSet(): FeaturePriceSet { + if (process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") { + return standardFeaturePriceSet; + } else { + return standardFeaturePriceSetSandbox; + } +} + +export function getLineItems(featurePriceSet: FeaturePriceSet): Stripe.Checkout.SessionCreateParams.LineItem[] { + return Object.entries(featurePriceSet).map(([featureId, priceId]) => ({ + price: priceId, + })); +} \ No newline at end of file diff --git a/server/lib/private/billing/index.ts b/server/lib/private/billing/index.ts new file mode 100644 index 00000000..0212ee1c --- /dev/null +++ b/server/lib/private/billing/index.ts @@ -0,0 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./limitSet"; +export * from "./features"; +export * from "./limitsService"; diff --git a/server/lib/private/billing/limitSet.ts b/server/lib/private/billing/limitSet.ts new file mode 100644 index 00000000..0cddb37c --- /dev/null +++ b/server/lib/private/billing/limitSet.ts @@ -0,0 +1,63 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { FeatureId } from "./features"; + +export type LimitSet = { + [key in FeatureId]: { + value: number | null; // null indicates no limit + description?: string; + }; +}; + +export const sandboxLimitSet: LimitSet = { + [FeatureId.SITE_UPTIME]: { value: 2880, description: "Sandbox limit" }, // 1 site up for 2 days + [FeatureId.USERS]: { value: 1, description: "Sandbox limit" }, + [FeatureId.EGRESS_DATA_MB]: { value: 1000, description: "Sandbox limit" }, // 1 GB + [FeatureId.DOMAINS]: { value: 0, description: "Sandbox limit" }, + [FeatureId.REMOTE_EXIT_NODES]: { value: 0, description: "Sandbox limit" }, +}; + +export const freeLimitSet: LimitSet = { + [FeatureId.SITE_UPTIME]: { value: 46080, description: "Free tier limit" }, // 1 site up for 32 days + [FeatureId.USERS]: { value: 3, description: "Free tier limit" }, + [FeatureId.EGRESS_DATA_MB]: { + value: 25000, + description: "Free tier limit" + }, // 25 GB + [FeatureId.DOMAINS]: { value: 3, description: "Free tier limit" }, + [FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Free tier limit" } +}; + +export const subscribedLimitSet: LimitSet = { + [FeatureId.SITE_UPTIME]: { + value: 2232000, + description: "Contact us to increase soft limit.", + }, // 50 sites up for 31 days + [FeatureId.USERS]: { + value: 150, + description: "Contact us to increase soft limit." + }, + [FeatureId.EGRESS_DATA_MB]: { + value: 12000000, + description: "Contact us to increase soft limit." + }, // 12000 GB + [FeatureId.DOMAINS]: { + value: 20, + description: "Contact us to increase soft limit." + }, + [FeatureId.REMOTE_EXIT_NODES]: { + value: 5, + description: "Contact us to increase soft limit." + } +}; diff --git a/server/lib/private/billing/limitsService.ts b/server/lib/private/billing/limitsService.ts new file mode 100644 index 00000000..168f5580 --- /dev/null +++ b/server/lib/private/billing/limitsService.ts @@ -0,0 +1,51 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { db, limits } from "@server/db"; +import { and, eq } from "drizzle-orm"; +import { LimitSet } from "./limitSet"; +import { FeatureId } from "./features"; + +class LimitService { + async applyLimitSetToOrg(orgId: string, limitSet: LimitSet): Promise { + const limitEntries = Object.entries(limitSet); + + // delete existing limits for the org + await db.transaction(async (trx) => { + await trx.delete(limits).where(eq(limits.orgId, orgId)); + for (const [featureId, entry] of limitEntries) { + const limitId = `${orgId}-${featureId}`; + const { value, description } = entry; + await trx + .insert(limits) + .values({ limitId, orgId, featureId, value, description }); + } + }); + } + + async getOrgLimit( + orgId: string, + featureId: FeatureId + ): Promise { + const limitId = `${orgId}-${featureId}`; + const [limit] = await db + .select() + .from(limits) + .where(and(eq(limits.limitId, limitId))) + .limit(1); + + return limit ? limit.value : null; + } +} + +export const limitsService = new LimitService(); diff --git a/server/lib/private/billing/tiers.ts b/server/lib/private/billing/tiers.ts new file mode 100644 index 00000000..e6322c9f --- /dev/null +++ b/server/lib/private/billing/tiers.ts @@ -0,0 +1,37 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export enum TierId { + STANDARD = "standard", +} + +export type TierPriceSet = { + [key in TierId]: string; +}; + +export const tierPriceSet: TierPriceSet = { // Free tier matches the freeLimitSet + [TierId.STANDARD]: "price_1RrQ9cD3Ee2Ir7Wmqdy3KBa0", +}; + +export const tierPriceSetSandbox: TierPriceSet = { // Free tier matches the freeLimitSet + // when matching tier the keys closer to 0 index are matched first so list the tiers in descending order of value + [TierId.STANDARD]: "price_1RrAYJDCpkOb237By2s1P32m", +}; + +export function getTierPriceSet(environment?: string, sandbox_mode?: boolean): TierPriceSet { + if ((process.env.ENVIRONMENT == "prod" && process.env.SANDBOX_MODE !== "true") || (environment === "prod" && sandbox_mode !== true)) { // THIS GETS LOADED CLIENT SIDE AND SERVER SIDE + return tierPriceSet; + } else { + return tierPriceSetSandbox; + } +} diff --git a/server/lib/private/billing/usageService.ts b/server/lib/private/billing/usageService.ts new file mode 100644 index 00000000..76099956 --- /dev/null +++ b/server/lib/private/billing/usageService.ts @@ -0,0 +1,889 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { eq, sql, and } from "drizzle-orm"; +import NodeCache from "node-cache"; +import { v4 as uuidv4 } from "uuid"; +import { PutObjectCommand } from "@aws-sdk/client-s3"; +import { s3Client } from "../s3"; +import * as fs from "fs/promises"; +import * as path from "path"; +import { + db, + usage, + customers, + sites, + newts, + limits, + Usage, + Limit, + Transaction +} from "@server/db"; +import { FeatureId, getFeatureMeterId } from "./features"; +import config from "@server/lib/config"; +import logger from "@server/logger"; +import { sendToClient } from "@server/routers/ws"; +import { build } from "@server/build"; + +interface StripeEvent { + identifier?: string; + timestamp: number; + event_name: string; + payload: { + value: number; + stripe_customer_id: string; + }; +} + +export class UsageService { + private cache: NodeCache; + private bucketName: string | undefined; + private currentEventFile: string | null = null; + private currentFileStartTime: number = 0; + private eventsDir: string | undefined; + private uploadingFiles: Set = new Set(); + + constructor() { + this.cache = new NodeCache({ stdTTL: 300 }); // 5 minute TTL + if (build !== "saas") { + return; + } + this.bucketName = config.getRawPrivateConfig().stripe?.s3Bucket; + this.eventsDir = config.getRawPrivateConfig().stripe?.localFilePath; + + // Ensure events directory exists + this.initializeEventsDirectory().then(() => { + this.uploadPendingEventFilesOnStartup(); + }); + + // Periodically check for old event files to upload + setInterval(() => { + this.uploadOldEventFiles().catch((err) => { + logger.error("Error in periodic event file upload:", err); + }); + }, 30000); // every 30 seconds + } + + /** + * Truncate a number to 11 decimal places to prevent precision issues + */ + private truncateValue(value: number): number { + return Math.round(value * 100000000000) / 100000000000; // 11 decimal places + } + + private async initializeEventsDirectory(): Promise { + if (!this.eventsDir) { + logger.warn("Stripe local file path is not configured, skipping events directory initialization."); + return; + } + try { + await fs.mkdir(this.eventsDir, { recursive: true }); + } catch (error) { + logger.error("Failed to create events directory:", error); + } + } + + private async uploadPendingEventFilesOnStartup(): Promise { + if (!this.eventsDir || !this.bucketName) { + logger.warn("Stripe local file path or bucket name is not configured, skipping leftover event file upload."); + return; + } + try { + const files = await fs.readdir(this.eventsDir); + for (const file of files) { + if (file.endsWith(".json")) { + const filePath = path.join(this.eventsDir, file); + try { + const fileContent = await fs.readFile( + filePath, + "utf-8" + ); + const events = JSON.parse(fileContent); + if (Array.isArray(events) && events.length > 0) { + // Upload to S3 + const uploadCommand = new PutObjectCommand({ + Bucket: this.bucketName, + Key: file, + Body: fileContent, + ContentType: "application/json" + }); + await s3Client.send(uploadCommand); + + // Check if file still exists before unlinking + try { + await fs.access(filePath); + await fs.unlink(filePath); + } catch (unlinkError) { + logger.debug(`Startup file ${file} was already deleted`); + } + + logger.info( + `Uploaded leftover event file ${file} to S3 with ${events.length} events` + ); + } else { + // Remove empty file + try { + await fs.access(filePath); + await fs.unlink(filePath); + } catch (unlinkError) { + logger.debug(`Empty startup file ${file} was already deleted`); + } + } + } catch (err) { + logger.error( + `Error processing leftover event file ${file}:`, + err + ); + } + } + } + } catch (err) { + logger.error("Failed to scan for leftover event files:", err); + } + } + + public async add( + orgId: string, + featureId: FeatureId, + value: number, + transaction: any = null + ): Promise { + if (build !== "saas") { + return null; + } + + // Truncate value to 11 decimal places + value = this.truncateValue(value); + + // Implement retry logic for deadlock handling + const maxRetries = 3; + let attempt = 0; + + while (attempt <= maxRetries) { + try { + // Get subscription data for this org (with caching) + const customerId = await this.getCustomerId(orgId, featureId); + + if (!customerId) { + logger.warn( + `No subscription data found for org ${orgId} and feature ${featureId}` + ); + return null; + } + + let usage; + if (transaction) { + usage = await this.internalAddUsage( + orgId, + featureId, + value, + transaction + ); + } else { + await db.transaction(async (trx) => { + usage = await this.internalAddUsage(orgId, featureId, value, trx); + }); + } + + // Log event for Stripe + await this.logStripeEvent(featureId, value, customerId); + + return usage || null; + } catch (error: any) { + // Check if this is a deadlock error + const isDeadlock = error?.code === '40P01' || + error?.cause?.code === '40P01' || + (error?.message && error.message.includes('deadlock')); + + if (isDeadlock && attempt < maxRetries) { + attempt++; + // Exponential backoff with jitter: 50-150ms, 100-300ms, 200-600ms + const baseDelay = Math.pow(2, attempt - 1) * 50; + const jitter = Math.random() * baseDelay; + const delay = baseDelay + jitter; + + logger.warn( + `Deadlock detected for ${orgId}/${featureId}, retrying attempt ${attempt}/${maxRetries} after ${delay.toFixed(0)}ms` + ); + + await new Promise(resolve => setTimeout(resolve, delay)); + continue; + } + + logger.error( + `Failed to add usage for ${orgId}/${featureId} after ${attempt} attempts:`, + error + ); + break; + } + } + + return null; + } + + private async internalAddUsage( + orgId: string, + featureId: FeatureId, + value: number, + trx: Transaction + ): Promise { + // Truncate value to 11 decimal places + value = this.truncateValue(value); + + const usageId = `${orgId}-${featureId}`; + const meterId = getFeatureMeterId(featureId); + + // Use upsert: insert if not exists, otherwise increment + const [returnUsage] = await trx + .insert(usage) + .values({ + usageId, + featureId, + orgId, + meterId, + latestValue: value, + updatedAt: Math.floor(Date.now() / 1000) + }) + .onConflictDoUpdate({ + target: usage.usageId, + set: { + latestValue: sql`${usage.latestValue} + ${value}` + } + }).returning(); + + return returnUsage; + } + + // Helper function to get today's date as string (YYYY-MM-DD) + getTodayDateString(): string { + return new Date().toISOString().split("T")[0]; + } + + // Helper function to get date string from Date object + getDateString(date: number): string { + return new Date(date * 1000).toISOString().split("T")[0]; + } + + async updateDaily( + orgId: string, + featureId: FeatureId, + value?: number, + customerId?: string + ): Promise { + if (build !== "saas") { + return; + } + try { + if (!customerId) { + customerId = + (await this.getCustomerId(orgId, featureId)) || undefined; + if (!customerId) { + logger.warn( + `No subscription data found for org ${orgId} and feature ${featureId}` + ); + return; + } + } + + // Truncate value to 11 decimal places if provided + if (value !== undefined && value !== null) { + value = this.truncateValue(value); + } + + const today = this.getTodayDateString(); + + let currentUsage: Usage | null = null; + + await db.transaction(async (trx) => { + // Get existing meter record + const usageId = `${orgId}-${featureId}`; + // Get current usage record + [currentUsage] = await trx + .select() + .from(usage) + .where(eq(usage.usageId, usageId)) + .limit(1); + + if (currentUsage) { + const lastUpdateDate = this.getDateString( + currentUsage.updatedAt + ); + const currentRunningTotal = currentUsage.latestValue; + const lastDailyValue = currentUsage.instantaneousValue || 0; + + if (value == undefined || value === null) { + value = currentUsage.instantaneousValue || 0; + } + + if (lastUpdateDate === today) { + // Same day update: replace the daily value + // Remove old daily value from running total, add new value + const newRunningTotal = this.truncateValue( + currentRunningTotal - lastDailyValue + value + ); + + await trx + .update(usage) + .set({ + latestValue: newRunningTotal, + instantaneousValue: value, + updatedAt: Math.floor(Date.now() / 1000) + }) + .where(eq(usage.usageId, usageId)); + } else { + // New day: add to running total + const newRunningTotal = this.truncateValue( + currentRunningTotal + value + ); + + await trx + .update(usage) + .set({ + latestValue: newRunningTotal, + instantaneousValue: value, + updatedAt: Math.floor(Date.now() / 1000) + }) + .where(eq(usage.usageId, usageId)); + } + } else { + // First record for this meter + const meterId = getFeatureMeterId(featureId); + const truncatedValue = this.truncateValue(value || 0); + await trx.insert(usage).values({ + usageId, + featureId, + orgId, + meterId, + instantaneousValue: truncatedValue, + latestValue: truncatedValue, + updatedAt: Math.floor(Date.now() / 1000) + }); + } + }); + + await this.logStripeEvent(featureId, value || 0, customerId); + } catch (error) { + logger.error( + `Failed to update daily usage for ${orgId}/${featureId}:`, + error + ); + } + } + + private async getCustomerId( + orgId: string, + featureId: FeatureId + ): Promise { + const cacheKey = `customer_${orgId}_${featureId}`; + const cached = this.cache.get(cacheKey); + + if (cached) { + return cached; + } + + try { + // Query subscription data + const [customer] = await db + .select({ + customerId: customers.customerId + }) + .from(customers) + .where(eq(customers.orgId, orgId)) + .limit(1); + + if (!customer) { + return null; + } + + const customerId = customer.customerId; + + // Cache the result + this.cache.set(cacheKey, customerId); + + return customerId; + } catch (error) { + logger.error( + `Failed to get subscription data for ${orgId}/${featureId}:`, + error + ); + return null; + } + } + + private async logStripeEvent( + featureId: FeatureId, + value: number, + customerId: string + ): Promise { + // Truncate value to 11 decimal places before sending to Stripe + const truncatedValue = this.truncateValue(value); + + const event: StripeEvent = { + identifier: uuidv4(), + timestamp: Math.floor(new Date().getTime() / 1000), + event_name: featureId, + payload: { + value: truncatedValue, + stripe_customer_id: customerId + } + }; + + await this.writeEventToFile(event); + await this.checkAndUploadFile(); + } + + private async writeEventToFile(event: StripeEvent): Promise { + if (!this.eventsDir || !this.bucketName) { + logger.warn("Stripe local file path or bucket name is not configured, skipping event file write."); + return; + } + if (!this.currentEventFile) { + this.currentEventFile = this.generateEventFileName(); + this.currentFileStartTime = Date.now(); + } + + const filePath = path.join(this.eventsDir, this.currentEventFile); + + try { + let events: StripeEvent[] = []; + + // Try to read existing file + try { + const fileContent = await fs.readFile(filePath, "utf-8"); + events = JSON.parse(fileContent); + } catch (error) { + // File doesn't exist or is empty, start with empty array + events = []; + } + + // Add new event + events.push(event); + + // Write back to file + await fs.writeFile(filePath, JSON.stringify(events, null, 2)); + } catch (error) { + logger.error("Failed to write event to file:", error); + } + } + + private async checkAndUploadFile(): Promise { + if (!this.currentEventFile) { + return; + } + + const now = Date.now(); + const fileAge = now - this.currentFileStartTime; + + // Check if file is at least 1 minute old + if (fileAge >= 60000) { + // 60 seconds + await this.uploadFileToS3(); + } + } + + private async uploadFileToS3(): Promise { + if (!this.bucketName || !this.eventsDir) { + logger.warn("Stripe local file path or bucket name is not configured, skipping S3 upload."); + return; + } + if (!this.currentEventFile) { + return; + } + + const fileName = this.currentEventFile; + const filePath = path.join(this.eventsDir, fileName); + + // Check if this file is already being uploaded + if (this.uploadingFiles.has(fileName)) { + logger.debug(`File ${fileName} is already being uploaded, skipping`); + return; + } + + // Mark file as being uploaded + this.uploadingFiles.add(fileName); + + try { + // Check if file exists before trying to read it + try { + await fs.access(filePath); + } catch (error) { + logger.debug(`File ${fileName} does not exist, may have been already processed`); + this.uploadingFiles.delete(fileName); + // Reset current file if it was this file + if (this.currentEventFile === fileName) { + this.currentEventFile = null; + this.currentFileStartTime = 0; + } + return; + } + + // Check if file exists and has content + const fileContent = await fs.readFile(filePath, "utf-8"); + const events = JSON.parse(fileContent); + + if (events.length === 0) { + // No events to upload, just clean up + try { + await fs.unlink(filePath); + } catch (unlinkError) { + // File may have been already deleted + logger.debug(`File ${fileName} was already deleted during cleanup`); + } + this.currentEventFile = null; + this.uploadingFiles.delete(fileName); + return; + } + + // Upload to S3 + const uploadCommand = new PutObjectCommand({ + Bucket: this.bucketName, + Key: fileName, + Body: fileContent, + ContentType: "application/json" + }); + + await s3Client.send(uploadCommand); + + // Clean up local file - check if it still exists before unlinking + try { + await fs.access(filePath); + await fs.unlink(filePath); + } catch (unlinkError) { + // File may have been already deleted by another process + logger.debug(`File ${fileName} was already deleted during upload`); + } + + logger.info( + `Uploaded ${fileName} to S3 with ${events.length} events` + ); + + // Reset for next file + this.currentEventFile = null; + this.currentFileStartTime = 0; + } catch (error) { + logger.error( + `Failed to upload ${fileName} to S3:`, + error + ); + } finally { + // Always remove from uploading set + this.uploadingFiles.delete(fileName); + } + } + + private generateEventFileName(): string { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + const uuid = uuidv4().substring(0, 8); + return `events-${timestamp}-${uuid}.json`; + } + + public async getUsage( + orgId: string, + featureId: FeatureId + ): Promise { + if (build !== "saas") { + return null; + } + + const usageId = `${orgId}-${featureId}`; + + try { + const [result] = await db + .select() + .from(usage) + .where(eq(usage.usageId, usageId)) + .limit(1); + + if (!result) { + // Lets create one if it doesn't exist using upsert to handle race conditions + logger.info( + `Creating new usage record for ${orgId}/${featureId}` + ); + const meterId = getFeatureMeterId(featureId); + + try { + const [newUsage] = await db + .insert(usage) + .values({ + usageId, + featureId, + orgId, + meterId, + latestValue: 0, + updatedAt: Math.floor(Date.now() / 1000) + }) + .onConflictDoNothing() + .returning(); + + if (newUsage) { + return newUsage; + } else { + // Record was created by another process, fetch it + const [existingUsage] = await db + .select() + .from(usage) + .where(eq(usage.usageId, usageId)) + .limit(1); + return existingUsage || null; + } + } catch (insertError) { + // Fallback: try to fetch existing record in case of any insert issues + logger.warn( + `Insert failed for ${orgId}/${featureId}, attempting to fetch existing record:`, + insertError + ); + const [existingUsage] = await db + .select() + .from(usage) + .where(eq(usage.usageId, usageId)) + .limit(1); + return existingUsage || null; + } + } + + return result + } catch (error) { + logger.error( + `Failed to get usage for ${orgId}/${featureId}:`, + error + ); + throw error; + } + } + + public async getUsageDaily( + orgId: string, + featureId: FeatureId + ): Promise { + if (build !== "saas") { + return null; + } + await this.updateDaily(orgId, featureId); // Ensure daily usage is updated + return this.getUsage(orgId, featureId); + } + + public async forceUpload(): Promise { + await this.uploadFileToS3(); + } + + public clearCache(): void { + this.cache.flushAll(); + } + + /** + * Scan the events directory for files older than 1 minute and upload them if not empty. + */ + private async uploadOldEventFiles(): Promise { + if (!this.eventsDir || !this.bucketName) { + logger.warn("Stripe local file path or bucket name is not configured, skipping old event file upload."); + return; + } + try { + const files = await fs.readdir(this.eventsDir); + const now = Date.now(); + for (const file of files) { + if (!file.endsWith(".json")) continue; + + // Skip files that are already being uploaded + if (this.uploadingFiles.has(file)) { + logger.debug(`Skipping file ${file} as it's already being uploaded`); + continue; + } + + const filePath = path.join(this.eventsDir, file); + + try { + // Check if file still exists before processing + try { + await fs.access(filePath); + } catch (accessError) { + logger.debug(`File ${file} does not exist, skipping`); + continue; + } + + const stat = await fs.stat(filePath); + const age = now - stat.mtimeMs; + if (age >= 90000) { + // 1.5 minutes - Mark as being uploaded + this.uploadingFiles.add(file); + + try { + const fileContent = await fs.readFile( + filePath, + "utf-8" + ); + const events = JSON.parse(fileContent); + if (Array.isArray(events) && events.length > 0) { + // Upload to S3 + const uploadCommand = new PutObjectCommand({ + Bucket: this.bucketName, + Key: file, + Body: fileContent, + ContentType: "application/json" + }); + await s3Client.send(uploadCommand); + + // Check if file still exists before unlinking + try { + await fs.access(filePath); + await fs.unlink(filePath); + } catch (unlinkError) { + logger.debug(`File ${file} was already deleted during interval upload`); + } + + logger.info( + `Interval: Uploaded event file ${file} to S3 with ${events.length} events` + ); + // If this was the current event file, reset it + if (this.currentEventFile === file) { + this.currentEventFile = null; + this.currentFileStartTime = 0; + } + } else { + // Remove empty file + try { + await fs.access(filePath); + await fs.unlink(filePath); + } catch (unlinkError) { + logger.debug(`Empty file ${file} was already deleted`); + } + } + } finally { + // Always remove from uploading set + this.uploadingFiles.delete(file); + } + } + } catch (err) { + logger.error( + `Interval: Error processing event file ${file}:`, + err + ); + // Remove from uploading set on error + this.uploadingFiles.delete(file); + } + } + } catch (err) { + logger.error("Interval: Failed to scan for event files:", err); + } + } + + public async checkLimitSet(orgId: string, kickSites = false, featureId?: FeatureId, usage?: Usage): Promise { + if (build !== "saas") { + return false; + } + // This method should check the current usage against the limits set for the organization + // and kick out all of the sites on the org + let hasExceededLimits = false; + + try { + let orgLimits: Limit[] = []; + if (featureId) { + // Get all limits set for this organization + orgLimits = await db + .select() + .from(limits) + .where( + and( + eq(limits.orgId, orgId), + eq(limits.featureId, featureId) + ) + ); + } else { + // Get all limits set for this organization + orgLimits = await db + .select() + .from(limits) + .where(eq(limits.orgId, orgId)); + } + + if (orgLimits.length === 0) { + logger.debug(`No limits set for org ${orgId}`); + return false; + } + + // Check each limit against current usage + for (const limit of orgLimits) { + let currentUsage: Usage | null; + if (usage) { + currentUsage = usage; + } else { + currentUsage = await this.getUsage(orgId, limit.featureId as FeatureId); + } + + const usageValue = currentUsage?.instantaneousValue || currentUsage?.latestValue || 0; + logger.debug(`Current usage for org ${orgId} on feature ${limit.featureId}: ${usageValue}`); + logger.debug(`Limit for org ${orgId} on feature ${limit.featureId}: ${limit.value}`); + if (currentUsage && limit.value !== null && usageValue > limit.value) { + logger.debug( + `Org ${orgId} has exceeded limit for ${limit.featureId}: ` + + `${usageValue} > ${limit.value}` + ); + hasExceededLimits = true; + break; // Exit early if any limit is exceeded + } + } + + // If any limits are exceeded, disconnect all sites for this organization + if (hasExceededLimits && kickSites) { + logger.warn(`Disconnecting all sites for org ${orgId} due to exceeded limits`); + + // Get all sites for this organization + const orgSites = await db + .select() + .from(sites) + .where(eq(sites.orgId, orgId)); + + // Mark all sites as offline and send termination messages + const siteUpdates = orgSites.map(site => site.siteId); + + if (siteUpdates.length > 0) { + // Send termination messages to newt sites + for (const site of orgSites) { + if (site.type === "newt") { + const [newt] = await db + .select() + .from(newts) + .where(eq(newts.siteId, site.siteId)) + .limit(1); + + if (newt) { + const payload = { + type: `newt/wg/terminate`, + data: { + reason: "Usage limits exceeded" + } + }; + + // Don't await to prevent blocking + sendToClient(newt.newtId, payload).catch((error: any) => { + logger.error( + `Failed to send termination message to newt ${newt.newtId}:`, + error + ); + }); + } + } + } + + logger.info(`Disconnected ${orgSites.length} sites for org ${orgId} due to exceeded limits`); + } + } + } catch (error) { + logger.error(`Error checking limits for org ${orgId}:`, error); + } + + return hasExceededLimits; + } +} + +export const usageService = new UsageService(); diff --git a/server/lib/private/createUserAccountOrg.ts b/server/lib/private/createUserAccountOrg.ts new file mode 100644 index 00000000..abde5ca7 --- /dev/null +++ b/server/lib/private/createUserAccountOrg.ts @@ -0,0 +1,206 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { isValidCIDR } from "@server/lib/validators"; +import { getNextAvailableOrgSubnet } from "@server/lib/ip"; +import { + actions, + apiKeyOrg, + apiKeys, + db, + domains, + Org, + orgDomains, + orgs, + roleActions, + roles, + userOrgs +} from "@server/db"; +import { eq } from "drizzle-orm"; +import { defaultRoleAllowedActions } from "@server/routers/role"; +import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/private/billing"; +import { createCustomer } from "@server/routers/private/billing/createCustomer"; +import { usageService } from "@server/lib/private/billing/usageService"; + +export async function createUserAccountOrg( + userId: string, + userEmail: string +): Promise<{ + success: boolean; + org?: { + orgId: string; + name: string; + subnet: string; + }; + error?: string; +}> { + // const subnet = await getNextAvailableOrgSubnet(); + const orgId = "org_" + userId; + const name = `${userEmail}'s Organization`; + + // if (!isValidCIDR(subnet)) { + // return { + // success: false, + // error: "Invalid subnet format. Please provide a valid CIDR notation." + // }; + // } + + // // make sure the subnet is unique + // const subnetExists = await db + // .select() + // .from(orgs) + // .where(eq(orgs.subnet, subnet)) + // .limit(1); + + // if (subnetExists.length > 0) { + // return { success: false, error: `Subnet ${subnet} already exists` }; + // } + + // make sure the orgId is unique + const orgExists = await db + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)) + .limit(1); + + if (orgExists.length > 0) { + return { + success: false, + error: `Organization with ID ${orgId} already exists` + }; + } + + let error = ""; + let org: Org | null = null; + + await db.transaction(async (trx) => { + const allDomains = await trx + .select() + .from(domains) + .where(eq(domains.configManaged, true)); + + const newOrg = await trx + .insert(orgs) + .values({ + orgId, + name, + // subnet + subnet: "100.90.128.0/24", // TODO: this should not be hardcoded - or can it be the same in all orgs? + createdAt: new Date().toISOString() + }) + .returning(); + + if (newOrg.length === 0) { + error = "Failed to create organization"; + trx.rollback(); + return; + } + + org = newOrg[0]; + + // Create admin role within the same transaction + const [insertedRole] = await trx + .insert(roles) + .values({ + orgId: newOrg[0].orgId, + isAdmin: true, + name: "Admin", + description: "Admin role with the most permissions" + }) + .returning({ roleId: roles.roleId }); + + if (!insertedRole || !insertedRole.roleId) { + error = "Failed to create Admin role"; + trx.rollback(); + return; + } + + const roleId = insertedRole.roleId; + + // Get all actions and create role actions + const actionIds = await trx.select().from(actions).execute(); + + if (actionIds.length > 0) { + await trx.insert(roleActions).values( + actionIds.map((action) => ({ + roleId, + actionId: action.actionId, + orgId: newOrg[0].orgId + })) + ); + } + + if (allDomains.length) { + await trx.insert(orgDomains).values( + allDomains.map((domain) => ({ + orgId: newOrg[0].orgId, + domainId: domain.domainId + })) + ); + } + + await trx.insert(userOrgs).values({ + userId, + orgId: newOrg[0].orgId, + roleId: roleId, + isOwner: true + }); + + const memberRole = await trx + .insert(roles) + .values({ + name: "Member", + description: "Members can only view resources", + orgId + }) + .returning(); + + await trx.insert(roleActions).values( + defaultRoleAllowedActions.map((action) => ({ + roleId: memberRole[0].roleId, + actionId: action, + orgId + })) + ); + }); + + await limitsService.applyLimitSetToOrg(orgId, sandboxLimitSet); + + if (!org) { + return { success: false, error: "Failed to create org" }; + } + + if (error) { + return { + success: false, + error: `Failed to create org: ${error}` + }; + } + + // make sure we have the stripe customer + const customerId = await createCustomer(orgId, userEmail); + + if (customerId) { + await usageService.updateDaily(orgId, FeatureId.USERS, 1, customerId); // Only 1 because we are crating the org + } + + return { + org: { + orgId, + name, + // subnet + subnet: "100.90.128.0/24" + }, + success: true + }; +} diff --git a/server/lib/private/rateLimitStore.ts b/server/lib/private/rateLimitStore.ts new file mode 100644 index 00000000..4700ba09 --- /dev/null +++ b/server/lib/private/rateLimitStore.ts @@ -0,0 +1,25 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import RedisStore from "@server/db/private/redisStore"; +import { MemoryStore, Store } from "express-rate-limit"; + +export function createStore(): Store { + const rateLimitStore: Store = new RedisStore({ + prefix: 'api-rate-limit', // Optional: customize Redis key prefix + skipFailedRequests: true, // Don't count failed requests + skipSuccessfulRequests: false, // Count successful requests + }); + + return rateLimitStore; +} diff --git a/server/lib/private/readConfigFile.ts b/server/lib/private/readConfigFile.ts new file mode 100644 index 00000000..ce2abcf4 --- /dev/null +++ b/server/lib/private/readConfigFile.ts @@ -0,0 +1,192 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import fs from "fs"; +import yaml from "js-yaml"; +import { privateConfigFilePath1 } from "@server/lib/consts"; +import { z } from "zod"; +import { colorsSchema } from "@server/lib/colorsSchema"; +import { build } from "@server/build"; + +const portSchema = z.number().positive().gt(0).lte(65535); + +export const privateConfigSchema = z + .object({ + app: z.object({ + region: z.string().optional().default("default"), + base_domain: z.string().optional() + }).optional().default({ + region: "default" + }), + server: z.object({ + encryption_key_path: z + .string() + .optional() + .default("./config/encryption.pem") + .pipe(z.string().min(8)), + resend_api_key: z.string().optional(), + reo_client_id: z.string().optional(), + }).optional().default({ + encryption_key_path: "./config/encryption.pem" + }), + redis: z + .object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0), + replicas: z + .array( + z.object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0) + }) + ) + .optional() + // tls: z + // .object({ + // reject_unauthorized: z + // .boolean() + // .optional() + // .default(true) + // }) + // .optional() + }) + .optional(), + gerbil: z + .object({ + local_exit_node_reachable_at: z.string().optional().default("http://gerbil:3003") + }) + .optional() + .default({}), + flags: z + .object({ + enable_redis: z.boolean().optional(), + hide_supporter_key: z.boolean().optional() + }) + .optional(), + branding: z + .object({ + app_name: z.string().optional(), + background_image_path: z.string().optional(), + colors: z + .object({ + light: colorsSchema.optional(), + dark: colorsSchema.optional() + }) + .optional(), + logo: z + .object({ + light_path: z.string().optional(), + dark_path: z.string().optional(), + auth_page: z + .object({ + width: z.number().optional(), + height: z.number().optional() + }) + .optional(), + navbar: z + .object({ + width: z.number().optional(), + height: z.number().optional() + }) + .optional() + }) + .optional(), + favicon_path: z.string().optional(), + footer: z + .array( + z.object({ + text: z.string(), + href: z.string().optional() + }) + ) + .optional(), + login_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + signup_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + resource_auth_page: z + .object({ + show_logo: z.boolean().optional(), + hide_powered_by: z.boolean().optional(), + title_text: z.string().optional(), + subtitle_text: z.string().optional() + }) + .optional(), + emails: z + .object({ + signature: z.string().optional(), + colors: z + .object({ + primary: z.string().optional() + }) + .optional() + }) + .optional() + }) + .optional(), + stripe: z + .object({ + secret_key: z.string(), + webhook_secret: z.string(), + s3Bucket: z.string(), + s3Region: z.string().default("us-east-1"), + localFilePath: z.string() + }) + .optional(), + }) + +export function readPrivateConfigFile() { + if (build == "oss") { + return {}; + } + + const loadConfig = (configPath: string) => { + try { + const yamlContent = fs.readFileSync(configPath, "utf8"); + const config = yaml.load(yamlContent); + return config; + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error loading configuration file: ${error.message}` + ); + } + throw error; + } + }; + + let environment: any; + if (fs.existsSync(privateConfigFilePath1)) { + environment = loadConfig(privateConfigFilePath1); + } + + if (!environment) { + throw new Error( + "No private configuration file found." + ); + } + + return environment; +} diff --git a/server/lib/private/resend.ts b/server/lib/private/resend.ts new file mode 100644 index 00000000..a502e98c --- /dev/null +++ b/server/lib/private/resend.ts @@ -0,0 +1,124 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Resend } from "resend"; +import config from "../config"; +import logger from "@server/logger"; + +export enum AudienceIds { + General = "5cfbf99b-c592-40a9-9b8a-577a4681c158", + Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20", + Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549" +} + +const resend = new Resend( + config.getRawPrivateConfig().server.resend_api_key || "missing" +); + +export default resend; + +export async function moveEmailToAudience( + email: string, + audienceId: AudienceIds +) { + if (process.env.ENVIRONMENT !== "prod") { + logger.debug(`Skipping moving email ${email} to audience ${audienceId} in non-prod environment`); + return; + } + const { error, data } = await retryWithBackoff(async () => { + const { data, error } = await resend.contacts.create({ + email, + unsubscribed: false, + audienceId + }); + if (error) { + throw new Error( + `Error adding email ${email} to audience ${audienceId}: ${error}` + ); + } + return { error, data }; + }); + + if (error) { + logger.error( + `Error adding email ${email} to audience ${audienceId}: ${error}` + ); + return; + } + + if (data) { + logger.debug( + `Added email ${email} to audience ${audienceId} with contact ID ${data.id}` + ) + } + + const otherAudiences = Object.values(AudienceIds).filter( + (id) => id !== audienceId + ); + + for (const otherAudienceId of otherAudiences) { + const { error, data } = await retryWithBackoff(async () => { + const { data, error } = await resend.contacts.remove({ + email, + audienceId: otherAudienceId + }); + if (error) { + throw new Error( + `Error removing email ${email} from audience ${otherAudienceId}: ${error}` + ); + } + return { error, data }; + }); + + if (error) { + logger.error( + `Error removing email ${email} from audience ${otherAudienceId}: ${error}` + ); + } + + if (data) { + logger.info( + `Removed email ${email} from audience ${otherAudienceId}` + ); + } + } +} + +type RetryOptions = { + retries?: number; + initialDelayMs?: number; + factor?: number; +}; + +export async function retryWithBackoff( + fn: () => Promise, + options: RetryOptions = {} +): Promise { + const { retries = 5, initialDelayMs = 500, factor = 2 } = options; + + let attempt = 0; + let delay = initialDelayMs; + + while (true) { + try { + return await fn(); + } catch (err) { + attempt++; + + if (attempt > retries) throw err; + + await new Promise((resolve) => setTimeout(resolve, delay)); + delay *= factor; + } + } +} diff --git a/server/lib/private/s3.ts b/server/lib/private/s3.ts new file mode 100644 index 00000000..26b1d49b --- /dev/null +++ b/server/lib/private/s3.ts @@ -0,0 +1,19 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { S3Client } from "@aws-sdk/client-s3"; +import config from "@server/lib/config"; + +export const s3Client = new S3Client({ + region: config.getRawPrivateConfig().stripe?.s3Region || "us-east-1", +}); diff --git a/server/lib/private/stripe.ts b/server/lib/private/stripe.ts new file mode 100644 index 00000000..1170202d --- /dev/null +++ b/server/lib/private/stripe.ts @@ -0,0 +1,28 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import config from "@server/lib/config"; +import logger from "@server/logger"; +import { build } from "@server/build"; + +let stripe: Stripe | undefined = undefined; +if (build == "saas") { + const stripeApiKey = config.getRawPrivateConfig().stripe?.secret_key; + if (!stripeApiKey) { + logger.error("Stripe secret key is not configured"); + } + stripe = new Stripe(stripeApiKey!); +} + +export default stripe; diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index 4ae80ac2..b70818ec 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -30,7 +30,7 @@ export const configSchema = z anonymous_usage: z.boolean().optional().default(true) }) .optional() - .default({}) + .default({}), }).optional().default({ log_level: "info", save_logs: false, @@ -130,7 +130,8 @@ export const configSchema = z secret: z .string() .pipe(z.string().min(8)) - .optional() + .optional(), + maxmind_db_path: z.string().optional() }).optional().default({ integration_port: 3003, external_port: 3000, diff --git a/server/lib/remoteCertificates/certificates.ts b/server/lib/remoteCertificates/certificates.ts index 9a4ce001..6404ee75 100644 --- a/server/lib/remoteCertificates/certificates.ts +++ b/server/lib/remoteCertificates/certificates.ts @@ -13,8 +13,8 @@ export async function getValidCertificatesForDomainsHybrid(domains: Set) wildcard: boolean | null; certFile: string | null; keyFile: string | null; - expiresAt: Date | null; - updatedAt?: Date | null; + expiresAt: number | null; + updatedAt?: number | null; }> > { if (domains.size === 0) { @@ -72,8 +72,8 @@ export async function getValidCertificatesForDomains(domains: Set): Prom wildcard: boolean | null; certFile: string | null; keyFile: string | null; - expiresAt: Date | null; - updatedAt?: Date | null; + expiresAt: number | null; + updatedAt?: number | null; }> > { return []; // stub diff --git a/server/lib/remoteCertificates/index.ts b/server/lib/remoteCertificates/index.ts index 53051b6c..fcd43d30 100644 --- a/server/lib/remoteCertificates/index.ts +++ b/server/lib/remoteCertificates/index.ts @@ -1 +1,14 @@ -export * from "./certificates"; \ No newline at end of file +import { build } from "@server/build"; + +// Import both modules +import * as certificateModule from "./certificates"; +import * as privateCertificateModule from "./privateCertificates"; + +// Conditionally export Remote Certificates implementation based on build type +const remoteCertificatesImplementation = build === "oss" ? certificateModule : privateCertificateModule; + +// Re-export all items from the selected implementation +export const { + getValidCertificatesForDomains, + getValidCertificatesForDomainsHybrid + } = remoteCertificatesImplementation; \ No newline at end of file diff --git a/server/lib/remoteCertificates/privateCertificates.ts b/server/lib/remoteCertificates/privateCertificates.ts new file mode 100644 index 00000000..fabc9ea5 --- /dev/null +++ b/server/lib/remoteCertificates/privateCertificates.ts @@ -0,0 +1,116 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import config from "../config"; +import { certificates, db } from "@server/db"; +import { and, eq, isNotNull } from "drizzle-orm"; +import { decryptData } from "../encryption"; +import * as fs from "fs"; + +export async function getValidCertificatesForDomains( + domains: Set +): Promise< + Array<{ + id: number; + domain: string; + wildcard: boolean | null; + certFile: string | null; + keyFile: string | null; + expiresAt: number | null; + updatedAt?: number | null; + }> +> { + if (domains.size === 0) { + return []; + } + + const domainArray = Array.from(domains); + + // TODO: add more foreign keys to make this query more efficient - we dont need to keep getting every certificate + const validCerts = await db + .select({ + id: certificates.certId, + domain: certificates.domain, + certFile: certificates.certFile, + keyFile: certificates.keyFile, + expiresAt: certificates.expiresAt, + updatedAt: certificates.updatedAt, + wildcard: certificates.wildcard + }) + .from(certificates) + .where( + and( + eq(certificates.status, "valid"), + isNotNull(certificates.certFile), + isNotNull(certificates.keyFile) + ) + ); + + // Filter certificates for the specified domains and if it is a wildcard then you can match on everything up to the first dot + const validCertsFiltered = validCerts.filter((cert) => { + return ( + domainArray.includes(cert.domain) || + (cert.wildcard && + domainArray.some((domain) => + domain.endsWith(`.${cert.domain}`) + )) + ); + }); + + const encryptionKeyPath = config.getRawPrivateConfig().server.encryption_key_path; + + if (!fs.existsSync(encryptionKeyPath)) { + throw new Error( + "Encryption key file not found. Please generate one first." + ); + } + + const encryptionKeyHex = fs.readFileSync(encryptionKeyPath, "utf8").trim(); + const encryptionKey = Buffer.from(encryptionKeyHex, "hex"); + + const validCertsDecrypted = validCertsFiltered.map((cert) => { + // Decrypt and save certificate file + const decryptedCert = decryptData( + cert.certFile!, // is not null from query + encryptionKey + ); + + // Decrypt and save key file + const decryptedKey = decryptData(cert.keyFile!, encryptionKey); + + // Return only the certificate data without org information + return { + ...cert, + certFile: decryptedCert, + keyFile: decryptedKey + }; + }); + + return validCertsDecrypted; +} + +export async function getValidCertificatesForDomainsHybrid( + domains: Set +): Promise< + Array<{ + id: number; + domain: string; + wildcard: boolean | null; + certFile: string | null; + keyFile: string | null; + expiresAt: number | null; + updatedAt?: number | null; + }> +> { + return []; // stub +} diff --git a/server/lib/schemas.ts b/server/lib/schemas.ts index 0888ff31..5e2bd400 100644 --- a/server/lib/schemas.ts +++ b/server/lib/schemas.ts @@ -17,3 +17,12 @@ export const tlsNameSchema = z ) .transform((val) => val.toLowerCase()); +export const privateNamespaceSubdomainSchema = z + .string() + .regex( + /^[a-zA-Z0-9-]+$/, + "Namespace subdomain can only contain letters, numbers, and hyphens" + ) + .min(1, "Namespace subdomain must be at least 1 character long") + .max(32, "Namespace subdomain must be at most 32 characters long") + .transform((val) => val.toLowerCase()); diff --git a/server/lib/traefikConfig.ts b/server/lib/traefik/TraefikConfigManager.ts similarity index 93% rename from server/lib/traefikConfig.ts rename to server/lib/traefik/TraefikConfigManager.ts index 8b133419..51466ecf 100644 --- a/server/lib/traefikConfig.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -6,16 +6,15 @@ import * as yaml from "js-yaml"; import axios from "axios"; import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { tokenManager } from "./tokenManager"; -import { - getCurrentExitNodeId, - getTraefikConfig -} from "@server/routers/traefik"; +import { tokenManager } from "../tokenManager"; +import { getCurrentExitNodeId } from "@server/lib/exitNodes"; +import { getTraefikConfig } from "./"; import { getValidCertificatesForDomains, getValidCertificatesForDomainsHybrid -} from "./remoteCertificates"; -import { sendToExitNode } from "./exitNodeComms"; +} from "../remoteCertificates"; +import { sendToExitNode } from "../exitNodes"; +import { build } from "@server/build"; export class TraefikConfigManager { private intervalId: NodeJS.Timeout | null = null; @@ -28,8 +27,8 @@ export class TraefikConfigManager { string, { exists: boolean; - lastModified: Date | null; - expiresAt: Date | null; + lastModified: number | null; + expiresAt: number | null; wildcard: boolean | null; } >(); @@ -115,8 +114,8 @@ export class TraefikConfigManager { string, { exists: boolean; - lastModified: Date | null; - expiresAt: Date | null; + lastModified: number | null; + expiresAt: number | null; wildcard: boolean; } > @@ -217,7 +216,12 @@ export class TraefikConfigManager { // Filter out domains covered by wildcard certificates const domainsNeedingCerts = new Set(); for (const domain of currentDomains) { - if (!isDomainCoveredByWildcard(domain, this.lastLocalCertificateState)) { + if ( + !isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { domainsNeedingCerts.add(domain); } } @@ -225,7 +229,12 @@ export class TraefikConfigManager { // Fetch if domains needing certificates have changed const lastDomainsNeedingCerts = new Set(); for (const domain of this.lastKnownDomains) { - if (!isDomainCoveredByWildcard(domain, this.lastLocalCertificateState)) { + if ( + !isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { lastDomainsNeedingCerts.add(domain); } } @@ -255,7 +264,7 @@ export class TraefikConfigManager { // Check if certificate is expiring soon (within 30 days) if (localState.expiresAt) { const daysUntilExpiry = - (localState.expiresAt.getTime() - Date.now()) / + (localState.expiresAt - Math.floor(Date.now() / 1000)) / (1000 * 60 * 60 * 24); if (daysUntilExpiry < 30) { logger.info( @@ -276,7 +285,7 @@ export class TraefikConfigManager { public async HandleTraefikConfig(): Promise { try { // Get all active domains for this exit node via HTTP call - const getTraefikConfig = await this.getTraefikConfig(); + const getTraefikConfig = await this.internalGetTraefikConfig(); if (!getTraefikConfig) { logger.error( @@ -315,15 +324,20 @@ export class TraefikConfigManager { wildcard: boolean | null; certFile: string | null; keyFile: string | null; - expiresAt: Date | null; - updatedAt?: Date | null; + expiresAt: number | null; + updatedAt?: number | null; }> = []; if (this.shouldFetchCertificates(domains)) { // Filter out domains that are already covered by wildcard certificates const domainsToFetch = new Set(); for (const domain of domains) { - if (!isDomainCoveredByWildcard(domain, this.lastLocalCertificateState)) { + if ( + !isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { domainsToFetch.add(domain); } else { logger.debug( @@ -339,7 +353,7 @@ export class TraefikConfigManager { await getValidCertificatesForDomainsHybrid( domainsToFetch ); - } else { + } else { validCertificates = await getValidCertificatesForDomains( domainsToFetch @@ -428,7 +442,7 @@ export class TraefikConfigManager { /** * Get all domains currently in use from traefik config API */ - private async getTraefikConfig(): Promise<{ + private async internalGetTraefikConfig(): Promise<{ domains: Set; traefikConfig: any; } | null> { @@ -451,9 +465,13 @@ export class TraefikConfigManager { traefikConfig = resp.data.data; } else { const currentExitNode = await getCurrentExitNodeId(); + // logger.debug(`Fetching traefik config for exit node: ${currentExitNode}`); traefikConfig = await getTraefikConfig( + // this is called by the local exit node to get its own config currentExitNode, - config.getRawConfig().traefik.site_types + config.getRawConfig().traefik.site_types, + build == "oss", // filter out the namespace domains in open source + build != "oss" // generate the login pages on the cloud and hybrid ); } @@ -621,7 +639,8 @@ export class TraefikConfigManager { } // If no exact match, check for wildcard certificates that cover this domain - for (const [certDomain, certState] of this.lastLocalCertificateState) { + for (const [certDomain, certState] of this + .lastLocalCertificateState) { if (certState.exists && certState.wildcard) { // Check if this wildcard certificate covers the domain if (domain.endsWith("." + certDomain)) { @@ -671,8 +690,8 @@ export class TraefikConfigManager { wildcard: boolean | null; certFile: string | null; keyFile: string | null; - expiresAt: Date | null; - updatedAt?: Date | null; + expiresAt: number | null; + updatedAt?: number | null; }> ): Promise { const dynamicConfigPath = @@ -758,7 +777,7 @@ export class TraefikConfigManager { // Update local state tracking this.lastLocalCertificateState.set(cert.domain, { exists: true, - lastModified: new Date(), + lastModified: Math.floor(Date.now() / 1000), expiresAt: cert.expiresAt, wildcard: cert.wildcard }); @@ -800,8 +819,8 @@ export class TraefikConfigManager { cert: { id: number; domain: string; - expiresAt: Date | null; - updatedAt?: Date | null; + expiresAt: number | null; + updatedAt?: number | null; }, certPath: string, keyPath: string, @@ -818,12 +837,12 @@ export class TraefikConfigManager { } // Read last update time from .last_update file - let lastUpdateTime: Date | null = null; + let lastUpdateTime: number | null = null; try { const lastUpdateStr = fs .readFileSync(lastUpdatePath, "utf8") .trim(); - lastUpdateTime = new Date(lastUpdateStr); + lastUpdateTime = Math.floor(new Date(lastUpdateStr).getTime() / 1000); } catch { lastUpdateTime = null; } @@ -1004,7 +1023,12 @@ export class TraefikConfigManager { // Find domains covered by wildcards for (const domain of this.activeDomains) { - if (isDomainCoveredByWildcard(domain, this.lastLocalCertificateState)) { + if ( + isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { domainsCoveredByWildcards.push(domain); } } @@ -1025,7 +1049,13 @@ export class TraefikConfigManager { /** * Check if a domain is covered by existing wildcard certificates */ -export function isDomainCoveredByWildcard(domain: string, lastLocalCertificateState: Map): boolean { +export function isDomainCoveredByWildcard( + domain: string, + lastLocalCertificateState: Map< + string, + { exists: boolean; wildcard: boolean | null } + > +): boolean { for (const [certDomain, state] of lastLocalCertificateState) { if (state.exists && state.wildcard) { // If stored as example.com but is wildcard, check subdomains diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts similarity index 76% rename from server/routers/traefik/getTraefikConfig.ts rename to server/lib/traefik/getTraefikConfig.ts index fa722ed5..598ce984 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -1,95 +1,14 @@ -import { Request, Response } from "express"; -import { db, exitNodes } from "@server/db"; +import { db, exitNodes, targetHealthCheck } from "@server/db"; import { and, eq, inArray, or, isNull, ne, isNotNull } from "drizzle-orm"; import logger from "@server/logger"; -import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; +import createPathRewriteMiddleware from "./middleware"; -let currentExitNodeId: number; const redirectHttpsMiddlewareName = "redirect-to-https"; const badgerMiddlewareName = "badger"; -export async function getCurrentExitNodeId(): Promise { - if (!currentExitNodeId) { - if (config.getRawConfig().gerbil.exit_node_name) { - const exitNodeName = config.getRawConfig().gerbil.exit_node_name!; - const [exitNode] = await db - .select({ - exitNodeId: exitNodes.exitNodeId - }) - .from(exitNodes) - .where(eq(exitNodes.name, exitNodeName)); - if (exitNode) { - currentExitNodeId = exitNode.exitNodeId; - } - } else { - const [exitNode] = await db - .select({ - exitNodeId: exitNodes.exitNodeId - }) - .from(exitNodes) - .limit(1); - - if (exitNode) { - currentExitNodeId = exitNode.exitNodeId; - } - } - } - return currentExitNodeId; -} - -export async function traefikConfigProvider( - _: Request, - res: Response -): Promise { - try { - // First query to get resources with site and org info - // Get the current exit node name from config - await getCurrentExitNodeId(); - - const traefikConfig = await getTraefikConfig( - currentExitNodeId, - config.getRawConfig().traefik.site_types - ); - - if (traefikConfig?.http?.middlewares) { - // BECAUSE SOMETIMES THE CONFIG CAN BE EMPTY IF THERE IS NOTHING - traefikConfig.http.middlewares[badgerMiddlewareName] = { - plugin: { - [badgerMiddlewareName]: { - apiBaseUrl: new URL( - "/api/v1", - `http://${ - config.getRawConfig().server.internal_hostname - }:${config.getRawConfig().server.internal_port}` - ).href, - userSessionCookieName: - config.getRawConfig().server.session_cookie_name, - - // deprecated - accessTokenQueryParam: - config.getRawConfig().server - .resource_access_token_param, - - resourceSessionRequestParam: - config.getRawConfig().server - .resource_session_request_param - } - } - }; - } - - return res.status(HttpCode.OK).json(traefikConfig); - } catch (e) { - logger.error(`Failed to build Traefik config: ${e}`); - return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({ - error: "Failed to build Traefik config" - }); - } -} - function validatePathRewriteConfig( path: string | null, @@ -157,140 +76,11 @@ function validatePathRewriteConfig( return { isValid: true }; } - -function createPathRewriteMiddleware( - middlewareName: string, - path: string, - pathMatchType: string, - rewritePath: string, - rewritePathType: string -): { middlewares: { [key: string]: any }; chain?: string[] } { - const middlewares: { [key: string]: any } = {}; - - if (pathMatchType !== "regex" && !path.startsWith("/")) { - path = `/${path}`; - } - - if (rewritePathType !== "regex" && rewritePath !== "" && !rewritePath.startsWith("/")) { - rewritePath = `/${rewritePath}`; - } - - switch (rewritePathType) { - case "exact": - // Replace the path with the exact rewrite path - let exactPattern = `^${escapeRegex(path)}$`; - middlewares[middlewareName] = { - replacePathRegex: { - regex: exactPattern, - replacement: rewritePath - } - }; - break; - - case "prefix": - // Replace matched prefix with new prefix, preserve the rest - switch (pathMatchType) { - case "prefix": - middlewares[middlewareName] = { - replacePathRegex: { - regex: `^${escapeRegex(path)}(.*)`, - replacement: `${rewritePath}$1` - } - }; - break; - case "exact": - middlewares[middlewareName] = { - replacePathRegex: { - regex: `^${escapeRegex(path)}$`, - replacement: rewritePath - } - }; - break; - case "regex": - // For regex path matching with prefix rewrite, we assume the regex has capture groups - middlewares[middlewareName] = { - replacePathRegex: { - regex: path, - replacement: rewritePath - } - }; - break; - } - break; - - case "regex": - // Use advanced regex replacement - works with any match type - let regexPattern: string; - if (pathMatchType === "regex") { - regexPattern = path; - } else if (pathMatchType === "prefix") { - regexPattern = `^${escapeRegex(path)}(.*)`; - } else { // exact - regexPattern = `^${escapeRegex(path)}$`; - } - - middlewares[middlewareName] = { - replacePathRegex: { - regex: regexPattern, - replacement: rewritePath - } - }; - break; - - case "stripPrefix": - // Strip the matched prefix and optionally add new path - if (pathMatchType === "prefix") { - middlewares[middlewareName] = { - stripPrefix: { - prefixes: [path] - } - }; - - // If rewritePath is provided and not empty, add it as a prefix after stripping - if (rewritePath && rewritePath !== "" && rewritePath !== "/") { - const addPrefixMiddlewareName = `addprefix-${middlewareName.replace('rewrite-', '')}`; - middlewares[addPrefixMiddlewareName] = { - addPrefix: { - prefix: rewritePath - } - }; - return { - middlewares, - chain: [middlewareName, addPrefixMiddlewareName] - }; - } - } else { - // For exact and regex matches, use replacePathRegex to strip - let regexPattern: string; - if (pathMatchType === "exact") { - regexPattern = `^${escapeRegex(path)}$`; - } else if (pathMatchType === "regex") { - regexPattern = path; - } else { - regexPattern = `^${escapeRegex(path)}`; - } - - const replacement = rewritePath || "/"; - middlewares[middlewareName] = { - replacePathRegex: { - regex: regexPattern, - replacement: replacement - } - }; - } - break; - - default: - logger.error(`Unknown rewritePathType: ${rewritePathType}`); - throw new Error(`Unknown rewritePathType: ${rewritePathType}`); - } - - return { middlewares }; -} - export async function getTraefikConfig( exitNodeId: number, - siteTypes: string[] + siteTypes: string[], + filterOutNamespaceDomains = false, + generateLoginPageRouters = false ): Promise { // Define extended target type with site information type TargetWithSite = Target & { @@ -329,6 +119,7 @@ export async function getTraefikConfig( method: targets.method, port: targets.port, internalPort: targets.internalPort, + hcHealth: targetHealthCheck.hcHealth, path: targets.path, pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, @@ -343,11 +134,19 @@ export async function getTraefikConfig( .from(sites) .innerJoin(targets, eq(targets.siteId, sites.siteId)) .innerJoin(resources, eq(resources.resourceId, targets.resourceId)) + .leftJoin( + targetHealthCheck, + eq(targetHealthCheck.targetId, targets.targetId) + ) .where( and( eq(targets.enabled, true), eq(resources.enabled, true), or(eq(sites.exitNodeId, exitNodeId), isNull(sites.exitNodeId)), + or( + ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets + isNull(targetHealthCheck.hcHealth) // Include targets with no health check record + ), inArray(sites.type, siteTypes), config.getRawConfig().traefik.allow_raw_resources ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true @@ -863,8 +662,4 @@ function sanitizeForMiddlewareName(str: string): string { // Replace any characters that aren't alphanumeric or dash with dash // and remove consecutive dashes return str.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); -} - -function escapeRegex(string: string): string { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } \ No newline at end of file diff --git a/server/lib/traefik/index.ts b/server/lib/traefik/index.ts new file mode 100644 index 00000000..1d654510 --- /dev/null +++ b/server/lib/traefik/index.ts @@ -0,0 +1,11 @@ +import { build } from "@server/build"; + +// Import both modules +import * as traefikModule from "./getTraefikConfig"; +import * as privateTraefikModule from "./privateGetTraefikConfig"; + +// Conditionally export Traefik configuration implementation based on build type +const traefikImplementation = build === "oss" ? traefikModule : privateTraefikModule; + +// Re-export all items from the selected implementation +export const { getTraefikConfig } = traefikImplementation; \ No newline at end of file diff --git a/server/lib/traefik/middleware.ts b/server/lib/traefik/middleware.ts new file mode 100644 index 00000000..e6660776 --- /dev/null +++ b/server/lib/traefik/middleware.ts @@ -0,0 +1,140 @@ +import logger from "@server/logger"; + +export default function createPathRewriteMiddleware( + middlewareName: string, + path: string, + pathMatchType: string, + rewritePath: string, + rewritePathType: string +): { middlewares: { [key: string]: any }; chain?: string[] } { + const middlewares: { [key: string]: any } = {}; + + if (pathMatchType !== "regex" && !path.startsWith("/")) { + path = `/${path}`; + } + + if ( + rewritePathType !== "regex" && + rewritePath !== "" && + !rewritePath.startsWith("/") + ) { + rewritePath = `/${rewritePath}`; + } + + switch (rewritePathType) { + case "exact": + // Replace the path with the exact rewrite path + let exactPattern = `^${escapeRegex(path)}$`; + middlewares[middlewareName] = { + replacePathRegex: { + regex: exactPattern, + replacement: rewritePath + } + }; + break; + + case "prefix": + // Replace matched prefix with new prefix, preserve the rest + switch (pathMatchType) { + case "prefix": + middlewares[middlewareName] = { + replacePathRegex: { + regex: `^${escapeRegex(path)}(.*)`, + replacement: `${rewritePath}$1` + } + }; + break; + case "exact": + middlewares[middlewareName] = { + replacePathRegex: { + regex: `^${escapeRegex(path)}$`, + replacement: rewritePath + } + }; + break; + case "regex": + // For regex path matching with prefix rewrite, we assume the regex has capture groups + middlewares[middlewareName] = { + replacePathRegex: { + regex: path, + replacement: rewritePath + } + }; + break; + } + break; + + case "regex": + // Use advanced regex replacement - works with any match type + let regexPattern: string; + if (pathMatchType === "regex") { + regexPattern = path; + } else if (pathMatchType === "prefix") { + regexPattern = `^${escapeRegex(path)}(.*)`; + } else { + // exact + regexPattern = `^${escapeRegex(path)}$`; + } + + middlewares[middlewareName] = { + replacePathRegex: { + regex: regexPattern, + replacement: rewritePath + } + }; + break; + + case "stripPrefix": + // Strip the matched prefix and optionally add new path + if (pathMatchType === "prefix") { + middlewares[middlewareName] = { + stripPrefix: { + prefixes: [path] + } + }; + + // If rewritePath is provided and not empty, add it as a prefix after stripping + if (rewritePath && rewritePath !== "" && rewritePath !== "/") { + const addPrefixMiddlewareName = `addprefix-${middlewareName.replace("rewrite-", "")}`; + middlewares[addPrefixMiddlewareName] = { + addPrefix: { + prefix: rewritePath + } + }; + return { + middlewares, + chain: [middlewareName, addPrefixMiddlewareName] + }; + } + } else { + // For exact and regex matches, use replacePathRegex to strip + let regexPattern: string; + if (pathMatchType === "exact") { + regexPattern = `^${escapeRegex(path)}$`; + } else if (pathMatchType === "regex") { + regexPattern = path; + } else { + regexPattern = `^${escapeRegex(path)}`; + } + + const replacement = rewritePath || "/"; + middlewares[middlewareName] = { + replacePathRegex: { + regex: regexPattern, + replacement: replacement + } + }; + } + break; + + default: + logger.error(`Unknown rewritePathType: ${rewritePathType}`); + throw new Error(`Unknown rewritePathType: ${rewritePathType}`); + } + + return { middlewares }; +} + +function escapeRegex(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts new file mode 100644 index 00000000..7f1ff614 --- /dev/null +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -0,0 +1,692 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response } from "express"; +import { + certificates, + db, + domainNamespaces, + exitNodes, + loginPage, + targetHealthCheck +} from "@server/db"; +import { and, eq, inArray, or, isNull, ne, isNotNull } from "drizzle-orm"; +import logger from "@server/logger"; +import HttpCode from "@server/types/HttpCode"; +import config from "@server/lib/config"; +import { orgs, resources, sites, Target, targets } from "@server/db"; +import { build } from "@server/build"; + +const redirectHttpsMiddlewareName = "redirect-to-https"; +const redirectToRootMiddlewareName = "redirect-to-root"; +const badgerMiddlewareName = "badger"; + +export async function getTraefikConfig( + exitNodeId: number, + siteTypes: string[], + filterOutNamespaceDomains = false, + generateLoginPageRouters = false +): Promise { + // Define extended target type with site information + type TargetWithSite = Target & { + site: { + siteId: number; + type: string; + subnet: string | null; + exitNodeId: number | null; + online: boolean; + }; + }; + + // Get resources with their targets and sites in a single optimized query + // Start from sites on this exit node, then join to targets and resources + const resourcesWithTargetsAndSites = await db + .select({ + // Resource fields + resourceId: resources.resourceId, + fullDomain: resources.fullDomain, + ssl: resources.ssl, + http: resources.http, + proxyPort: resources.proxyPort, + protocol: resources.protocol, + subdomain: resources.subdomain, + domainId: resources.domainId, + enabled: resources.enabled, + stickySession: resources.stickySession, + tlsServerName: resources.tlsServerName, + setHostHeader: resources.setHostHeader, + enableProxy: resources.enableProxy, + headers: resources.headers, + // Target fields + targetId: targets.targetId, + targetEnabled: targets.enabled, + ip: targets.ip, + method: targets.method, + port: targets.port, + internalPort: targets.internalPort, + hcHealth: targetHealthCheck.hcHealth, + path: targets.path, + pathMatchType: targets.pathMatchType, + + // Site fields + siteId: sites.siteId, + siteType: sites.type, + siteOnline: sites.online, + subnet: sites.subnet, + exitNodeId: sites.exitNodeId, + // Namespace + domainNamespaceId: domainNamespaces.domainNamespaceId, + // Certificate + certificateStatus: certificates.status + }) + .from(sites) + .innerJoin(targets, eq(targets.siteId, sites.siteId)) + .innerJoin(resources, eq(resources.resourceId, targets.resourceId)) + .leftJoin(certificates, eq(certificates.domainId, resources.domainId)) + .leftJoin( + targetHealthCheck, + eq(targetHealthCheck.targetId, targets.targetId) + ) + .leftJoin( + domainNamespaces, + eq(domainNamespaces.domainId, resources.domainId) + ) // THIS IS CLOUD ONLY TO FILTER OUT THE DOMAIN NAMESPACES IF REQUIRED + .where( + and( + eq(targets.enabled, true), + eq(resources.enabled, true), + // or( + eq(sites.exitNodeId, exitNodeId), + // isNull(sites.exitNodeId) + // ), + or( + ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets + isNull(targetHealthCheck.hcHealth) // Include targets with no health check record + ), + inArray(sites.type, siteTypes), + config.getRawConfig().traefik.allow_raw_resources + ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true + : eq(resources.http, true) + ) + ); + + // Group by resource and include targets with their unique site data + const resourcesMap = new Map(); + + resourcesWithTargetsAndSites.forEach((row) => { + const resourceId = row.resourceId; + const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths + const pathMatchType = row.pathMatchType || ""; + + if (filterOutNamespaceDomains && row.domainNamespaceId) { + return; + } + + // Create a unique key combining resourceId and path+pathMatchType + const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-"); + const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); + + if (!resourcesMap.has(mapKey)) { + resourcesMap.set(mapKey, { + resourceId: row.resourceId, + fullDomain: row.fullDomain, + ssl: row.ssl, + http: row.http, + proxyPort: row.proxyPort, + protocol: row.protocol, + subdomain: row.subdomain, + domainId: row.domainId, + enabled: row.enabled, + stickySession: row.stickySession, + tlsServerName: row.tlsServerName, + setHostHeader: row.setHostHeader, + enableProxy: row.enableProxy, + certificateStatus: row.certificateStatus, + targets: [], + headers: row.headers, + path: row.path, // the targets will all have the same path + pathMatchType: row.pathMatchType // the targets will all have the same pathMatchType + }); + } + + // Add target with its associated site data + resourcesMap.get(mapKey).targets.push({ + resourceId: row.resourceId, + targetId: row.targetId, + ip: row.ip, + method: row.method, + port: row.port, + internalPort: row.internalPort, + enabled: row.targetEnabled, + site: { + siteId: row.siteId, + type: row.siteType, + subnet: row.subnet, + exitNodeId: row.exitNodeId, + online: row.siteOnline + } + }); + }); + + // make sure we have at least one resource + if (resourcesMap.size === 0) { + return {}; + } + + const config_output: any = { + http: { + middlewares: { + [redirectHttpsMiddlewareName]: { + redirectScheme: { + scheme: "https" + } + }, + [redirectToRootMiddlewareName]: { + redirectRegex: { + regex: "^(https?)://([^/]+)(/.*)?", + replacement: "${1}://${2}/auth/org", + permanent: false + } + } + } + } + }; + + // get the key and the resource + for (const [key, resource] of resourcesMap.entries()) { + const targets = resource.targets; + + const routerName = `${key}-router`; + const serviceName = `${key}-service`; + const fullDomain = `${resource.fullDomain}`; + const transportName = `${key}-transport`; + const headersMiddlewareName = `${key}-headers-middleware`; + + if (!resource.enabled) { + continue; + } + + if (resource.http) { + if (!resource.domainId) { + continue; + } + + if (!resource.fullDomain) { + continue; + } + + if (resource.certificateStatus !== "valid") { + logger.debug( + `Resource ${resource.resourceId} has certificate stats ${resource.certificateStats}` + ); + continue; + } + + // add routers and services empty objects if they don't exist + if (!config_output.http.routers) { + config_output.http.routers = {}; + } + + if (!config_output.http.services) { + config_output.http.services = {}; + } + + const domainParts = fullDomain.split("."); + let wildCard; + if (domainParts.length <= 2) { + wildCard = `*.${domainParts.join(".")}`; + } else { + wildCard = `*.${domainParts.slice(1).join(".")}`; + } + + if (!resource.subdomain) { + wildCard = resource.fullDomain; + } + + const configDomain = config.getDomain(resource.domainId); + + let certResolver: string, preferWildcardCert: boolean; + if (!configDomain) { + certResolver = config.getRawConfig().traefik.cert_resolver; + preferWildcardCert = + config.getRawConfig().traefik.prefer_wildcard_cert; + } else { + certResolver = configDomain.cert_resolver; + preferWildcardCert = configDomain.prefer_wildcard_cert; + } + + let tls = {}; + if (build == "oss") { + tls = { + certResolver: certResolver, + ...(preferWildcardCert + ? { + domains: [ + { + main: wildCard + } + ] + } + : {}) + }; + } + + const additionalMiddlewares = + config.getRawConfig().traefik.additional_middlewares || []; + + const routerMiddlewares = [ + badgerMiddlewareName, + ...additionalMiddlewares + ]; + + if (resource.headers || resource.setHostHeader) { + // if there are headers, parse them into an object + const headersObj: { [key: string]: string } = {}; + if (resource.headers) { + let headersArr: { name: string; value: string }[] = []; + try { + headersArr = JSON.parse(resource.headers) as { + name: string; + value: string; + }[]; + } catch (e) { + logger.warn( + `Failed to parse headers for resource ${resource.resourceId}: ${e}` + ); + } + + headersArr.forEach((header) => { + headersObj[header.name] = header.value; + }); + } + + if (resource.setHostHeader) { + headersObj["Host"] = resource.setHostHeader; + } + + // check if the object is not empty + if (Object.keys(headersObj).length > 0) { + // Add the headers middleware + if (!config_output.http.middlewares) { + config_output.http.middlewares = {}; + } + config_output.http.middlewares[headersMiddlewareName] = { + headers: { + customRequestHeaders: headersObj + } + }; + + routerMiddlewares.push(headersMiddlewareName); + } + } + + let rule = `Host(\`${fullDomain}\`)`; + let priority = 100; + if (resource.path && resource.pathMatchType) { + priority += 1; + // add path to rule based on match type + let path = resource.path; + // if the path doesn't start with a /, add it + if (!path.startsWith("/")) { + path = `/${path}`; + } + if (resource.pathMatchType === "exact") { + rule += ` && Path(\`${path}\`)`; + } else if (resource.pathMatchType === "prefix") { + rule += ` && PathPrefix(\`${path}\`)`; + } else if (resource.pathMatchType === "regex") { + rule += ` && PathRegexp(\`${resource.path}\`)`; // this is the raw path because it's a regex + } + } + + config_output.http.routers![routerName] = { + entryPoints: [ + resource.ssl + ? config.getRawConfig().traefik.https_entrypoint + : config.getRawConfig().traefik.http_entrypoint + ], + middlewares: routerMiddlewares, + service: serviceName, + rule: rule, + priority: priority, + ...(resource.ssl ? { tls } : {}) + }; + + if (resource.ssl) { + config_output.http.routers![routerName + "-redirect"] = { + entryPoints: [ + config.getRawConfig().traefik.http_entrypoint + ], + middlewares: [redirectHttpsMiddlewareName], + service: serviceName, + rule: rule, + priority: priority + }; + } + + config_output.http.services![serviceName] = { + loadBalancer: { + servers: (() => { + // Check if any sites are online + // THIS IS SO THAT THERE IS SOME IMMEDIATE FEEDBACK + // EVEN IF THE SITES HAVE NOT UPDATED YET FROM THE + // RECEIVE BANDWIDTH ENDPOINT. + + // TODO: HOW TO HANDLE ^^^^^^ BETTER + const anySitesOnline = ( + targets as TargetWithSite[] + ).some((target: TargetWithSite) => target.site.online); + + return ( + (targets as TargetWithSite[]) + .filter((target: TargetWithSite) => { + if (!target.enabled) { + return false; + } + + // If any sites are online, exclude offline sites + if (anySitesOnline && !target.site.online) { + return false; + } + + if ( + target.site.type === "local" || + target.site.type === "wireguard" + ) { + if ( + !target.ip || + !target.port || + !target.method + ) { + return false; + } + } else if (target.site.type === "newt") { + if ( + !target.internalPort || + !target.method || + !target.site.subnet + ) { + return false; + } + } + return true; + }) + .map((target: TargetWithSite) => { + if ( + target.site.type === "local" || + target.site.type === "wireguard" + ) { + return { + url: `${target.method}://${target.ip}:${target.port}` + }; + } else if (target.site.type === "newt") { + const ip = + target.site.subnet!.split("/")[0]; + return { + url: `${target.method}://${ip}:${target.internalPort}` + }; + } + }) + // filter out duplicates + .filter( + (v, i, a) => + a.findIndex( + (t) => t && v && t.url === v.url + ) === i + ) + ); + })(), + ...(resource.stickySession + ? { + sticky: { + cookie: { + name: "p_sticky", // TODO: make this configurable via config.yml like other cookies + secure: resource.ssl, + httpOnly: true + } + } + } + : {}) + } + }; + + // Add the serversTransport if TLS server name is provided + if (resource.tlsServerName) { + if (!config_output.http.serversTransports) { + config_output.http.serversTransports = {}; + } + config_output.http.serversTransports![transportName] = { + serverName: resource.tlsServerName, + //unfortunately the following needs to be set. traefik doesn't merge the default serverTransport settings + // if defined in the static config and here. if not set, self-signed certs won't work + insecureSkipVerify: true + }; + config_output.http.services![ + serviceName + ].loadBalancer.serversTransport = transportName; + } + } else { + // Non-HTTP (TCP/UDP) configuration + if (!resource.enableProxy) { + continue; + } + + const protocol = resource.protocol.toLowerCase(); + const port = resource.proxyPort; + + if (!port) { + continue; + } + + if (!config_output[protocol]) { + config_output[protocol] = { + routers: {}, + services: {} + }; + } + + config_output[protocol].routers[routerName] = { + entryPoints: [`${protocol}-${port}`], + service: serviceName, + ...(protocol === "tcp" ? { rule: "HostSNI(`*`)" } : {}) + }; + + config_output[protocol].services[serviceName] = { + loadBalancer: { + servers: (() => { + // Check if any sites are online + const anySitesOnline = ( + targets as TargetWithSite[] + ).some((target: TargetWithSite) => target.site.online); + + return (targets as TargetWithSite[]) + .filter((target: TargetWithSite) => { + if (!target.enabled) { + return false; + } + + // If any sites are online, exclude offline sites + if (anySitesOnline && !target.site.online) { + return false; + } + + if ( + target.site.type === "local" || + target.site.type === "wireguard" + ) { + if (!target.ip || !target.port) { + return false; + } + } else if (target.site.type === "newt") { + if ( + !target.internalPort || + !target.site.subnet + ) { + return false; + } + } + return true; + }) + .map((target: TargetWithSite) => { + if ( + target.site.type === "local" || + target.site.type === "wireguard" + ) { + return { + address: `${target.ip}:${target.port}` + }; + } else if (target.site.type === "newt") { + const ip = + target.site.subnet!.split("/")[0]; + return { + address: `${ip}:${target.internalPort}` + }; + } + }); + })(), + ...(resource.stickySession + ? { + sticky: { + ipStrategy: { + depth: 0, + sourcePort: true + } + } + } + : {}) + } + }; + } + } + + if (generateLoginPageRouters) { + const exitNodeLoginPages = await db + .select({ + loginPageId: loginPage.loginPageId, + fullDomain: loginPage.fullDomain, + exitNodeId: exitNodes.exitNodeId, + domainId: loginPage.domainId, + certificateStatus: certificates.status + }) + .from(loginPage) + .innerJoin( + exitNodes, + eq(exitNodes.exitNodeId, loginPage.exitNodeId) + ) + .leftJoin( + certificates, + eq(certificates.domainId, loginPage.domainId) + ) + .where(eq(exitNodes.exitNodeId, exitNodeId)); + + if (exitNodeLoginPages.length > 0) { + if (!config_output.http.services) { + config_output.http.services = {}; + } + + if (!config_output.http.services["landing-service"]) { + config_output.http.services["landing-service"] = { + loadBalancer: { + servers: [ + { + url: `http://${ + config.getRawConfig().server + .internal_hostname + }:${config.getRawConfig().server.next_port}` + } + ] + } + }; + } + + for (const lp of exitNodeLoginPages) { + if (!lp.domainId) { + continue; + } + + if (!lp.fullDomain) { + continue; + } + + if (lp.certificateStatus !== "valid") { + continue; + } + + // auth-allowed: + // rule: "Host(`auth.pangolin.internal`) && (PathRegexp(`^/auth/resource/[0-9]+$`) || PathPrefix(`/_next`))" + // service: next-service + // entryPoints: + // - websecure + + const routerName = `loginpage-${lp.loginPageId}`; + const fullDomain = `${lp.fullDomain}`; + + if (!config_output.http.routers) { + config_output.http.routers = {}; + } + + config_output.http.routers![routerName + "-router"] = { + entryPoints: [ + config.getRawConfig().traefik.https_entrypoint + ], + service: "landing-service", + rule: `Host(\`${fullDomain}\`) && (PathRegexp(\`^/auth/resource/[^/]+$\`) || PathRegexp(\`^/auth/idp/[0-9]+/oidc/callback\`) || PathPrefix(\`/_next\`) || Path(\`/auth/org\`) || PathRegexp(\`^/__nextjs*\`))`, + priority: 203, + tls: {} + }; + + // auth-catchall: + // rule: "Host(`auth.example.com`)" + // middlewares: + // - redirect-to-root + // service: next-service + // entryPoints: + // - web + + config_output.http.routers![routerName + "-catchall"] = { + entryPoints: [ + config.getRawConfig().traefik.https_entrypoint + ], + middlewares: [redirectToRootMiddlewareName], + service: "landing-service", + rule: `Host(\`${fullDomain}\`)`, + priority: 202, + tls: {} + }; + + // we need to add a redirect from http to https too + config_output.http.routers![routerName + "-redirect"] = { + entryPoints: [ + config.getRawConfig().traefik.http_entrypoint + ], + middlewares: [redirectHttpsMiddlewareName], + service: "landing-service", + rule: `Host(\`${fullDomain}\`)`, + priority: 201 + }; + } + } + } + + return config_output; +} + +function sanitizePath(path: string | null | undefined): string | undefined { + if (!path) return undefined; + // clean any non alphanumeric characters from the path and replace with dashes + // the path cant be too long either, so limit to 50 characters + if (path.length > 50) { + path = path.substring(0, 50); + } + return path.replace(/[^a-zA-Z0-9]/g, ""); +} diff --git a/server/lib/traefikConfig.test.ts b/server/lib/traefik/traefikConfig.test.ts similarity index 99% rename from server/lib/traefikConfig.test.ts rename to server/lib/traefik/traefikConfig.test.ts index 55d19647..88e5da49 100644 --- a/server/lib/traefikConfig.test.ts +++ b/server/lib/traefik/traefikConfig.test.ts @@ -1,5 +1,5 @@ import { assertEquals } from "@test/assert"; -import { isDomainCoveredByWildcard } from "./traefikConfig"; +import { isDomainCoveredByWildcard } from "./TraefikConfigManager"; function runTests() { console.log('Running wildcard domain coverage tests...'); diff --git a/server/lib/validators.ts b/server/lib/validators.ts index 522e5018..59776105 100644 --- a/server/lib/validators.ts +++ b/server/lib/validators.ts @@ -163,6 +163,26 @@ export function validateHeaders(headers: string): boolean { }); } +export function isSecondLevelDomain(domain: string): boolean { + if (!domain || typeof domain !== 'string') { + return false; + } + + const trimmedDomain = domain.trim().toLowerCase(); + + // Split into parts + const parts = trimmedDomain.split('.'); + + // Should have exactly 2 parts for a second-level domain (e.g., "example.com") + if (parts.length !== 2) { + return false; + } + + // Check if the TLD part is valid + const tld = parts[1].toUpperCase(); + return validTlds.includes(tld); +} + const validTlds = [ "AAA", "AARP", diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts index 28a73afd..f211fa9e 100644 --- a/server/middlewares/index.ts +++ b/server/middlewares/index.ts @@ -27,4 +27,4 @@ export * from "./verifyApiKeyAccess"; export * from "./verifyDomainAccess"; export * from "./verifyClientsEnabled"; export * from "./verifyUserIsOrgOwner"; -export * from "./verifySiteResourceAccess"; +export * from "./verifySiteResourceAccess"; \ No newline at end of file diff --git a/server/middlewares/private/corsWithLoginPage.ts b/server/middlewares/private/corsWithLoginPage.ts new file mode 100644 index 00000000..03725c50 --- /dev/null +++ b/server/middlewares/private/corsWithLoginPage.ts @@ -0,0 +1,98 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import cors, { CorsOptions } from "cors"; +import config from "@server/lib/config"; +import logger from "@server/logger"; +import { db, loginPage } from "@server/db"; +import { eq } from "drizzle-orm"; + +async function isValidLoginPageDomain(host: string): Promise { + try { + const [result] = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, host)) + .limit(1); + + const isValid = !!result; + + return isValid; + } catch (error) { + logger.error("Error checking loginPage domain:", error); + return false; + } +} + +export function corsWithLoginPageSupport(corsConfig: any) { + const options = { + ...(corsConfig?.origins + ? { origin: corsConfig.origins } + : { + origin: (origin: any, callback: any) => { + callback(null, true); + } + }), + ...(corsConfig?.methods && { methods: corsConfig.methods }), + ...(corsConfig?.allowed_headers && { + allowedHeaders: corsConfig.allowed_headers + }), + credentials: !(corsConfig?.credentials === false) + }; + + return async (req: Request, res: Response, next: NextFunction) => { + const originValidatedCorsConfig = { + origin: async ( + origin: string | undefined, + callback: (err: Error | null, allow?: boolean) => void + ) => { + // If no origin (e.g., same-origin request), allow it + + if (!origin) { + return callback(null, true); + } + + const dashboardUrl = config.getRawConfig().app.dashboard_url; + + // If no dashboard_url is configured, allow all origins + if (!dashboardUrl) { + return callback(null, true); + } + + // Check if origin matches dashboard URL + const dashboardHost = new URL(dashboardUrl).host; + const originHost = new URL(origin).host; + + if (originHost === dashboardHost) { + return callback(null, true); + } + + // If origin doesn't match dashboard URL, check if it's a valid loginPage domain + const isValidDomain = await isValidLoginPageDomain(originHost); + + if (isValidDomain) { + return callback(null, true); + } + + // Origin is not valid + return callback(null, false); + }, + methods: corsConfig?.methods, + allowedHeaders: corsConfig?.allowed_headers, + credentials: corsConfig?.credentials !== false + } as CorsOptions; + + return cors(originValidatedCorsConfig)(req, res, next); + }; +} diff --git a/server/middlewares/private/index.ts b/server/middlewares/private/index.ts new file mode 100644 index 00000000..f034001d --- /dev/null +++ b/server/middlewares/private/index.ts @@ -0,0 +1,18 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./verifyCertificateAccess"; +export * from "./verifyRemoteExitNodeAccess"; +export * from "./verifyIdpAccess"; +export * from "./verifyLoginPageAccess"; +export * from "./corsWithLoginPage"; \ No newline at end of file diff --git a/server/middlewares/private/verifyCertificateAccess.ts b/server/middlewares/private/verifyCertificateAccess.ts new file mode 100644 index 00000000..1708215e --- /dev/null +++ b/server/middlewares/private/verifyCertificateAccess.ts @@ -0,0 +1,126 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { db, domainNamespaces } from "@server/db"; +import { certificates } from "@server/db"; +import { domains, orgDomains } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; +import logger from "@server/logger"; + +export async function verifyCertificateAccess( + req: Request, + res: Response, + next: NextFunction +) { + try { + // Assume user/org access is already verified + const orgId = req.params.orgId; + const certId = req.params.certId || req.body?.certId || req.query?.certId; + let domainId = + req.params.domainId || req.body?.domainId || req.query?.domainId; + + if (!orgId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID") + ); + } + + if (!domainId) { + + if (!certId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId") + ); + } + + // Get the certificate and its domainId + const [cert] = await db + .select() + .from(certificates) + .where(eq(certificates.certId, Number(certId))) + .limit(1); + + if (!cert) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Certificate with ID ${certId} not found` + ) + ); + } + + domainId = cert.domainId; + if (!domainId) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Certificate with ID ${certId} does not have a domain` + ) + ); + } + } + + if (!domainId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId") + ); + } + + // Check if the domain is a namespace domain + const [namespaceDomain] = await db + .select() + .from(domainNamespaces) + .where(eq(domainNamespaces.domainId, domainId)) + .limit(1); + + if (namespaceDomain) { + // If it's a namespace domain, we can skip the org check + return next(); + } + + // Check if the domain is associated with the org + const [orgDomain] = await db + .select() + .from(orgDomains) + .where( + and( + eq(orgDomains.orgId, orgId), + eq(orgDomains.domainId, domainId) + ) + ) + .limit(1); + + if (!orgDomain) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Organization does not have access to this certificate" + ) + ); + } + + return next(); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying certificate access" + ) + ); + } +} +export default verifyCertificateAccess; diff --git a/server/middlewares/private/verifyIdpAccess.ts b/server/middlewares/private/verifyIdpAccess.ts new file mode 100644 index 00000000..87397a3d --- /dev/null +++ b/server/middlewares/private/verifyIdpAccess.ts @@ -0,0 +1,102 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { userOrgs, db, idp, idpOrg } from "@server/db"; +import { and, eq, or } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +export async function verifyIdpAccess( + req: Request, + res: Response, + next: NextFunction +) { + try { + const userId = req.user!.userId; + const idpId = + req.params.idpId || req.body.idpId || req.query.idpId; + const orgId = req.params.orgId; + + if (!userId) { + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); + } + + if (!orgId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID") + ); + } + + if (!idpId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid key ID") + ); + } + + const [idpRes] = await db + .select() + .from(idp) + .innerJoin(idpOrg, eq(idp.idpId, idpOrg.idpId)) + .where( + and(eq(idp.idpId, idpId), eq(idpOrg.orgId, orgId)) + ) + .limit(1); + + if (!idpRes || !idpRes.idp || !idpRes.idpOrg) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `IdP with ID ${idpId} not found for organization ${orgId}` + ) + ); + } + + if (!req.userOrg) { + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, idpRes.idpOrg.orgId) + ) + ) + .limit(1); + req.userOrg = userOrgRole[0]; + } + + if (!req.userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + const userOrgRoleId = req.userOrg.roleId; + req.userOrgRoleId = userOrgRoleId; + + return next(); + } catch (error) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying idp access" + ) + ); + } +} diff --git a/server/middlewares/private/verifyLoginPageAccess.ts b/server/middlewares/private/verifyLoginPageAccess.ts new file mode 100644 index 00000000..bc9e8713 --- /dev/null +++ b/server/middlewares/private/verifyLoginPageAccess.ts @@ -0,0 +1,81 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { userOrgs, db, loginPageOrg } from "@server/db"; +import { and, eq, inArray } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +export async function verifyLoginPageAccess( + req: Request, + res: Response, + next: NextFunction +) { + try { + const userId = req.user!.userId; + const loginPageId = + req.params.loginPageId || + req.body.loginPageId || + req.query.loginPageId; + + if (!userId) { + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); + } + + if (!loginPageId) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid login page ID") + ); + } + + const loginPageOrgs = await db + .select({ + orgId: loginPageOrg.orgId + }) + .from(loginPageOrg) + .where(eq(loginPageOrg.loginPageId, loginPageId)); + + const orgIds = loginPageOrgs.map((lpo) => lpo.orgId); + + const existingUserOrgs = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + inArray(userOrgs.orgId, orgIds) + ) + ); + + if (existingUserOrgs.length === 0) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Login page with ID ${loginPageId} not found for user's organizations` + ) + ); + } + + return next(); + } catch (error) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying login page access" + ) + ); + } +} diff --git a/server/middlewares/private/verifyRemoteExitNode.ts b/server/middlewares/private/verifyRemoteExitNode.ts new file mode 100644 index 00000000..45c244e2 --- /dev/null +++ b/server/middlewares/private/verifyRemoteExitNode.ts @@ -0,0 +1,56 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Response } from "express"; +import ErrorResponse from "@server/types/ErrorResponse"; +import config from "@server/lib/config"; +import { unauthorized } from "@server/auth/unauthorizedResponse"; +import logger from "@server/logger"; +import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; + +export const verifySessionRemoteExitNodeMiddleware = async ( + req: any, + res: Response, + next: NextFunction +) => { + // get the token from the auth header + const token = req.headers["authorization"]?.split(" ")[1] || ""; + + const { session, remoteExitNode } = await validateRemoteExitNodeSessionToken(token); + + if (!session || !remoteExitNode) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info(`Remote exit node session not found. IP: ${req.ip}.`); + } + return next(unauthorized()); + } + + // const existingUser = await db + // .select() + // .from(users) + // .where(eq(users.userId, user.userId)); + + // if (!existingUser || !existingUser[0]) { + // if (config.getRawConfig().app.log_failed_attempts) { + // logger.info(`User session not found. IP: ${req.ip}.`); + // } + // return next( + // createHttpError(HttpCode.BAD_REQUEST, "User does not exist") + // ); + // } + + req.session = session; + req.remoteExitNode = remoteExitNode; + + next(); +}; diff --git a/server/middlewares/private/verifyRemoteExitNodeAccess.ts b/server/middlewares/private/verifyRemoteExitNodeAccess.ts new file mode 100644 index 00000000..a2cd2bac --- /dev/null +++ b/server/middlewares/private/verifyRemoteExitNodeAccess.ts @@ -0,0 +1,118 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { db, exitNodeOrgs, exitNodes, remoteExitNodes } from "@server/db"; +import { sites, userOrgs, userSites, roleSites, roles } from "@server/db"; +import { and, eq, or } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +export async function verifyRemoteExitNodeAccess( + req: Request, + res: Response, + next: NextFunction +) { + const userId = req.user!.userId; // Assuming you have user information in the request + const orgId = req.params.orgId; + const remoteExitNodeId = + req.params.remoteExitNodeId || + req.body.remoteExitNodeId || + req.query.remoteExitNodeId; + + if (!userId) { + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); + } + + try { + const [remoteExitNode] = await db + .select() + .from(remoteExitNodes) + .where(and(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId))); + + if (!remoteExitNode) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Remote exit node with ID ${remoteExitNodeId} not found` + ) + ); + } + + if (!remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `Remote exit node with ID ${remoteExitNodeId} does not have an exit node ID` + ) + ); + } + + const [exitNodeOrg] = await db + .select() + .from(exitNodeOrgs) + .where( + and( + eq(exitNodeOrgs.exitNodeId, remoteExitNode.exitNodeId), + eq(exitNodeOrgs.orgId, orgId) + ) + ); + + if (!exitNodeOrg) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Remote exit node with ID ${remoteExitNodeId} not found in organization ${orgId}` + ) + ); + } + + if (!req.userOrg) { + // Get user's role ID in the organization + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, exitNodeOrg.orgId) + ) + ) + .limit(1); + req.userOrg = userOrgRole[0]; + } + + if (!req.userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + const userOrgRoleId = req.userOrg.roleId; + req.userOrgRoleId = userOrgRoleId; + + return next(); + } catch (error) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying remote exit node access" + ) + ); + } +} diff --git a/server/middlewares/verifyOrgAccess.ts b/server/middlewares/verifyOrgAccess.ts index a2cc44f2..521f1002 100644 --- a/server/middlewares/verifyOrgAccess.ts +++ b/server/middlewares/verifyOrgAccess.ts @@ -37,7 +37,7 @@ export async function verifyOrgAccess( } if (!req.userOrg) { - next( + return next( createHttpError( HttpCode.FORBIDDEN, "User does not have access to this organization" diff --git a/server/routers/auth/changePassword.ts b/server/routers/auth/changePassword.ts index 3a9120e3..64efb696 100644 --- a/server/routers/auth/changePassword.ts +++ b/server/routers/auth/changePassword.ts @@ -6,7 +6,7 @@ import { z } from "zod"; import { db } from "@server/db"; import { User, users } from "@server/db"; import { eq } from "drizzle-orm"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { hashPassword, verifyPassword diff --git a/server/routers/auth/checkResourceSession.ts b/server/routers/auth/checkResourceSession.ts index ca7d80cc..9840d564 100644 --- a/server/routers/auth/checkResourceSession.ts +++ b/server/routers/auth/checkResourceSession.ts @@ -3,7 +3,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { validateResourceSessionToken } from "@server/auth/sessions/resource"; import logger from "@server/logger"; diff --git a/server/routers/auth/disable2fa.ts b/server/routers/auth/disable2fa.ts index 7fbea2e5..da19c0d7 100644 --- a/server/routers/auth/disable2fa.ts +++ b/server/routers/auth/disable2fa.ts @@ -6,7 +6,7 @@ import { z } from "zod"; import { db } from "@server/db"; import { User, users } from "@server/db"; import { eq } from "drizzle-orm"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { verifyPassword } from "@server/auth/password"; import { verifyTotpCode } from "@server/auth/totp"; import logger from "@server/logger"; diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 505d12c2..9db5931a 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -10,7 +10,10 @@ export * from "./resetPassword"; export * from "./requestPasswordReset"; export * from "./setServerAdmin"; export * from "./initialSetupComplete"; +export * from "./privateQuickStart"; export * from "./validateSetupToken"; export * from "./changePassword"; export * from "./checkResourceSession"; export * from "./securityKey"; +export * from "./privateGetSessionTransferToken"; +export * from "./privateTransferSession"; diff --git a/server/routers/auth/initialSetupComplete.ts b/server/routers/auth/initialSetupComplete.ts index 8da9acd7..2b616c97 100644 --- a/server/routers/auth/initialSetupComplete.ts +++ b/server/routers/auth/initialSetupComplete.ts @@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db, users } from "@server/db"; import { eq } from "drizzle-orm"; diff --git a/server/routers/auth/privateGetSessionTransferToken.ts b/server/routers/auth/privateGetSessionTransferToken.ts new file mode 100644 index 00000000..ba295923 --- /dev/null +++ b/server/routers/auth/privateGetSessionTransferToken.ts @@ -0,0 +1,97 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, sessionTransferToken } from "@server/db"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import { fromError } from "zod-validation-error"; +import logger from "@server/logger"; +import { + generateSessionToken, + SESSION_COOKIE_NAME +} from "@server/auth/sessions/app"; +import { encodeHexLowerCase } from "@oslojs/encoding"; +import { sha256 } from "@oslojs/crypto/sha2"; +import { response } from "@server/lib/response"; +import { encrypt } from "@server/lib/crypto"; +import config from "@server/lib/config"; + +const paramsSchema = z.object({}).strict(); + +export type GetSessionTransferTokenRenponse = { + token: string; +}; + +export async function getSessionTransferToken( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { user, session } = req; + + if (!user || !session) { + return next(createHttpError(HttpCode.UNAUTHORIZED, "Unauthorized")); + } + + const tokenRaw = generateSessionToken(); + const token = encodeHexLowerCase( + sha256(new TextEncoder().encode(tokenRaw)) + ); + + const rawSessionId = req.cookies[SESSION_COOKIE_NAME]; + + if (!rawSessionId) { + return next(createHttpError(HttpCode.UNAUTHORIZED, "Unauthorized")); + } + + const encryptedSession = encrypt( + rawSessionId, + config.getRawConfig().server.secret! + ); + + await db.insert(sessionTransferToken).values({ + encryptedSession, + token, + sessionId: session.sessionId, + expiresAt: Date.now() + 30 * 1000 // Token valid for 30 seconds + }); + + return response(res, { + data: { + token: tokenRaw + }, + success: true, + error: false, + message: "Transfer token created successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/auth/privateQuickStart.ts b/server/routers/auth/privateQuickStart.ts new file mode 100644 index 00000000..bdb95e5b --- /dev/null +++ b/server/routers/auth/privateQuickStart.ts @@ -0,0 +1,581 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { + account, + db, + domainNamespaces, + domains, + exitNodes, + newts, + newtSessions, + orgs, + passwordResetTokens, + Resource, + resourcePassword, + resourcePincode, + resources, + resourceWhitelist, + roleResources, + roles, + roleSites, + sites, + targetHealthCheck, + targets, + userResources, + userSites +} from "@server/db"; +import HttpCode from "@server/types/HttpCode"; +import { z } from "zod"; +import { users } from "@server/db"; +import { fromError } from "zod-validation-error"; +import createHttpError from "http-errors"; +import response from "@server/lib/response"; +import { SqliteError } from "better-sqlite3"; +import { eq, and, sql } from "drizzle-orm"; +import moment from "moment"; +import { generateId } from "@server/auth/sessions/app"; +import config from "@server/lib/config"; +import logger from "@server/logger"; +import { hashPassword } from "@server/auth/password"; +import { UserType } from "@server/types/UserTypes"; +import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; +import { sendEmail } from "@server/emails"; +import WelcomeQuickStart from "@server/emails/templates/WelcomeQuickStart"; +import { alphabet, generateRandomString } from "oslo/crypto"; +import { createDate, TimeSpan } from "oslo"; +import { getUniqueResourceName, getUniqueSiteName } from "@server/db/names"; +import { pickPort } from "../target/helpers"; +import { addTargets } from "../newt/targets"; +import { isTargetValid } from "@server/lib/validators"; +import { listExitNodes } from "@server/lib/exitNodes"; + +const bodySchema = z.object({ + email: z.string().toLowerCase().email(), + ip: z.string().refine(isTargetValid), + method: z.enum(["http", "https"]), + port: z.number().int().min(1).max(65535), + pincode: z + .string() + .regex(/^\d{6}$/) + .optional(), + password: z.string().min(4).max(100).optional(), + enableWhitelist: z.boolean().optional().default(true), + animalId: z.string() // This is actually the secret key for the backend +}); + +export type QuickStartBody = z.infer; + +export type QuickStartResponse = { + newtId: string; + newtSecret: string; + resourceUrl: string; + completeSignUpLink: string; +}; + +const DEMO_UBO_KEY = "b460293f-347c-4b30-837d-4e06a04d5a22"; + +export async function quickStart( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = bodySchema.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { + email, + ip, + method, + port, + pincode, + password, + enableWhitelist, + animalId + } = parsedBody.data; + + try { + const tokenValidation = validateTokenOnApi(animalId); + + if (!tokenValidation.isValid) { + logger.warn( + `Quick start failed for ${email} token ${animalId}: ${tokenValidation.message}` + ); + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Invalid or expired token" + ) + ); + } + + if (animalId === DEMO_UBO_KEY) { + if (email !== "mehrdad@getubo.com") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Invalid email for demo Ubo key" + ) + ); + } + + const [existing] = await db + .select() + .from(users) + .where( + and( + eq(users.email, email), + eq(users.type, UserType.Internal) + ) + ); + + if (existing) { + // delete the user if it already exists + await db.delete(users).where(eq(users.userId, existing.userId)); + const orgId = `org_${existing.userId}`; + await db.delete(orgs).where(eq(orgs.orgId, orgId)); + } + } + + const tempPassword = generateId(15); + const passwordHash = await hashPassword(tempPassword); + const userId = generateId(15); + + // TODO: see if that user already exists? + + // Create the sandbox user + const existing = await db + .select() + .from(users) + .where( + and(eq(users.email, email), eq(users.type, UserType.Internal)) + ); + + if (existing && existing.length > 0) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "A user with that email address already exists" + ) + ); + } + + let newtId: string; + let secret: string; + let fullDomain: string; + let resource: Resource; + let orgId: string; + let completeSignUpLink: string; + + await db.transaction(async (trx) => { + await trx.insert(users).values({ + userId: userId, + type: UserType.Internal, + username: email, + email: email, + passwordHash, + dateCreated: moment().toISOString() + }); + + // create user"s account + await trx.insert(account).values({ + userId + }); + }); + + const { success, error, org } = await createUserAccountOrg( + userId, + email + ); + if (!success) { + if (error) { + throw new Error(error); + } + throw new Error("Failed to create user account and organization"); + } + if (!org) { + throw new Error("Failed to create user account and organization"); + } + + orgId = org.orgId; + + await db.transaction(async (trx) => { + const token = generateRandomString( + 8, + alphabet("0-9", "A-Z", "a-z") + ); + + await trx + .delete(passwordResetTokens) + .where(eq(passwordResetTokens.userId, userId)); + + const tokenHash = await hashPassword(token); + + await trx.insert(passwordResetTokens).values({ + userId: userId, + email: email, + tokenHash, + expiresAt: createDate(new TimeSpan(7, "d")).getTime() + }); + + // // Create the sandbox newt + // const newClientAddress = await getNextAvailableClientSubnet(orgId); + // if (!newClientAddress) { + // throw new Error("No available subnet found"); + // } + + // const clientAddress = newClientAddress.split("/")[0]; + + newtId = generateId(15); + secret = generateId(48); + + // Create the sandbox site + const siteNiceId = await getUniqueSiteName(orgId); + const siteName = `First Site`; + let siteId: number | undefined; + + // pick a random exit node + const exitNodesList = await listExitNodes(orgId); + + // select a random exit node + const randomExitNode = + exitNodesList[Math.floor(Math.random() * exitNodesList.length)]; + + if (!randomExitNode) { + throw new Error("No exit nodes available"); + } + + const [newSite] = await trx + .insert(sites) + .values({ + orgId, + exitNodeId: randomExitNode.exitNodeId, + name: siteName, + niceId: siteNiceId, + // address: clientAddress, + type: "newt", + dockerSocketEnabled: true + }) + .returning(); + + siteId = newSite.siteId; + + const adminRole = await trx + .select() + .from(roles) + .where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId))) + .limit(1); + + if (adminRole.length === 0) { + throw new Error("Admin role not found"); + } + + await trx.insert(roleSites).values({ + roleId: adminRole[0].roleId, + siteId: newSite.siteId + }); + + if (req.user && req.userOrgRoleId != adminRole[0].roleId) { + // make sure the user can access the site + await trx.insert(userSites).values({ + userId: req.user?.userId!, + siteId: newSite.siteId + }); + } + + // add the peer to the exit node + const secretHash = await hashPassword(secret!); + + await trx.insert(newts).values({ + newtId: newtId!, + secretHash, + siteId: newSite.siteId, + dateCreated: moment().toISOString() + }); + + const [randomNamespace] = await trx + .select() + .from(domainNamespaces) + .orderBy(sql`RANDOM()`) + .limit(1); + + if (!randomNamespace) { + throw new Error("No domain namespace available"); + } + + const [randomNamespaceDomain] = await trx + .select() + .from(domains) + .where(eq(domains.domainId, randomNamespace.domainId)) + .limit(1); + + if (!randomNamespaceDomain) { + throw new Error("No domain found for the namespace"); + } + + const resourceNiceId = await getUniqueResourceName(orgId); + + // Create sandbox resource + const subdomain = `${resourceNiceId}-${generateId(5)}`; + fullDomain = `${subdomain}.${randomNamespaceDomain.baseDomain}`; + + const resourceName = `First Resource`; + + const newResource = await trx + .insert(resources) + .values({ + niceId: resourceNiceId, + fullDomain, + domainId: randomNamespaceDomain.domainId, + orgId, + name: resourceName, + subdomain, + http: true, + protocol: "tcp", + ssl: true, + sso: false, + emailWhitelistEnabled: enableWhitelist + }) + .returning(); + + await trx.insert(roleResources).values({ + roleId: adminRole[0].roleId, + resourceId: newResource[0].resourceId + }); + + if (req.user && req.userOrgRoleId != adminRole[0].roleId) { + // make sure the user can access the resource + await trx.insert(userResources).values({ + userId: req.user?.userId!, + resourceId: newResource[0].resourceId + }); + } + + resource = newResource[0]; + + // Create the sandbox target + const { internalPort, targetIps } = await pickPort(siteId!, trx); + + if (!internalPort) { + throw new Error("No available internal port"); + } + + const newTarget = await trx + .insert(targets) + .values({ + resourceId: resource.resourceId, + siteId: siteId!, + internalPort, + ip, + method, + port, + enabled: true + }) + .returning(); + + const newHealthcheck = await trx + .insert(targetHealthCheck) + .values({ + targetId: newTarget[0].targetId, + hcEnabled: false + }).returning(); + + // add the new target to the targetIps array + targetIps.push(`${ip}/32`); + + const [newt] = await trx + .select() + .from(newts) + .where(eq(newts.siteId, siteId!)) + .limit(1); + + await addTargets(newt.newtId, newTarget, newHealthcheck, resource.protocol); + + // Set resource pincode if provided + if (pincode) { + await trx + .delete(resourcePincode) + .where( + eq(resourcePincode.resourceId, resource!.resourceId) + ); + + const pincodeHash = await hashPassword(pincode); + + await trx.insert(resourcePincode).values({ + resourceId: resource!.resourceId, + pincodeHash, + digitLength: 6 + }); + } + + // Set resource password if provided + if (password) { + await trx + .delete(resourcePassword) + .where( + eq(resourcePassword.resourceId, resource!.resourceId) + ); + + const passwordHash = await hashPassword(password); + + await trx.insert(resourcePassword).values({ + resourceId: resource!.resourceId, + passwordHash + }); + } + + // Set resource OTP if whitelist is enabled + if (enableWhitelist) { + await trx.insert(resourceWhitelist).values({ + email, + resourceId: resource!.resourceId + }); + } + + completeSignUpLink = `${config.getRawConfig().app.dashboard_url}/auth/reset-password?quickstart=true&email=${email}&token=${token}`; + + // Store token for email outside transaction + await sendEmail( + WelcomeQuickStart({ + username: email, + link: completeSignUpLink, + fallbackLink: `${config.getRawConfig().app.dashboard_url}/auth/reset-password?quickstart=true&email=${email}`, + resourceMethod: method, + resourceHostname: ip, + resourcePort: port, + resourceUrl: `https://${fullDomain}`, + cliCommand: `newt --id ${newtId} --secret ${secret}` + }), + { + to: email, + from: config.getNoReplyEmail(), + subject: `Access your Pangolin dashboard and resources` + } + ); + }); + + return response(res, { + data: { + newtId: newtId!, + newtSecret: secret!, + resourceUrl: `https://${fullDomain!}`, + completeSignUpLink: completeSignUpLink! + }, + success: true, + error: false, + message: "Quick start completed successfully", + status: HttpCode.OK + }); + } catch (e) { + if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Account already exists with that email. Email: ${email}. IP: ${req.ip}.` + ); + } + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "A user with that email address already exists" + ) + ); + } else { + logger.error(e); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to do quick start" + ) + ); + } + } +} + +const BACKEND_SECRET_KEY = "4f9b6000-5d1a-11f0-9de7-ff2cc032f501"; + +/** + * Validates a token received from the frontend. + * @param {string} token The validation token from the request. + * @returns {{ isValid: boolean; message: string }} An object indicating if the token is valid. + */ +const validateTokenOnApi = ( + token: string +): { isValid: boolean; message: string } => { + if (token === DEMO_UBO_KEY) { + // Special case for demo UBO key + return { isValid: true, message: "Demo UBO key is valid." }; + } + + if (!token) { + return { isValid: false, message: "Error: No token provided." }; + } + + try { + // 1. Decode the base64 string + const decodedB64 = atob(token); + + // 2. Reverse the character code manipulation + const deobfuscated = decodedB64 + .split("") + .map((char) => String.fromCharCode(char.charCodeAt(0) - 5)) // Reverse the shift + .join(""); + + // 3. Split the data to get the original secret and timestamp + const parts = deobfuscated.split("|"); + if (parts.length !== 2) { + throw new Error("Invalid token format."); + } + const receivedKey = parts[0]; + const tokenTimestamp = parseInt(parts[1], 10); + + // 4. Check if the secret key matches + if (receivedKey !== BACKEND_SECRET_KEY) { + return { isValid: false, message: "Invalid token: Key mismatch." }; + } + + // 5. Check if the timestamp is recent (e.g., within 30 seconds) to prevent replay attacks + const now = Date.now(); + const timeDifference = now - tokenTimestamp; + + if (timeDifference > 30000) { + // 30 seconds + return { isValid: false, message: "Invalid token: Expired." }; + } + + if (timeDifference < 0) { + // Timestamp is in the future + return { + isValid: false, + message: "Invalid token: Timestamp is in the future." + }; + } + + // If all checks pass, the token is valid + return { isValid: true, message: "Token is valid!" }; + } catch (error) { + // This will catch errors from atob (if not valid base64) or other issues. + return { + isValid: false, + message: `Error: ${(error as Error).message}` + }; + } +}; diff --git a/server/routers/auth/privateTransferSession.ts b/server/routers/auth/privateTransferSession.ts new file mode 100644 index 00000000..e75f77dd --- /dev/null +++ b/server/routers/auth/privateTransferSession.ts @@ -0,0 +1,128 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import HttpCode from "@server/types/HttpCode"; +import { NextFunction, Request, Response } from "express"; +import createHttpError from "http-errors"; +import { z } from "zod"; +import { fromError } from "zod-validation-error"; +import logger from "@server/logger"; +import { sessions, sessionTransferToken } from "@server/db"; +import { db } from "@server/db"; +import { eq } from "drizzle-orm"; +import { response } from "@server/lib/response"; +import { encodeHexLowerCase } from "@oslojs/encoding"; +import { sha256 } from "@oslojs/crypto/sha2"; +import { serializeSessionCookie } from "@server/auth/sessions/app"; +import { decrypt } from "@server/lib/crypto"; +import config from "@server/lib/config"; + +const bodySchema = z.object({ + token: z.string() +}); + +export type TransferSessionBodySchema = z.infer; + +export type TransferSessionResponse = { + valid: boolean; + cookie?: string; +}; + +export async function transferSession( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = bodySchema.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + try { + const { token } = parsedBody.data; + + const tokenRaw = encodeHexLowerCase( + sha256(new TextEncoder().encode(token)) + ); + + const [existing] = await db + .select() + .from(sessionTransferToken) + .where(eq(sessionTransferToken.token, tokenRaw)) + .innerJoin( + sessions, + eq(sessions.sessionId, sessionTransferToken.sessionId) + ) + .limit(1); + + if (!existing) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid transfer token") + ); + } + + const transferToken = existing.sessionTransferToken; + const session = existing.session; + + if (!transferToken) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid transfer token") + ); + } + + await db + .delete(sessionTransferToken) + .where(eq(sessionTransferToken.token, tokenRaw)); + + if (Date.now() > transferToken.expiresAt) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Transfer token expired") + ); + } + + const rawSession = decrypt( + transferToken.encryptedSession, + config.getRawConfig().server.secret! + ); + + const isSecure = req.protocol === "https"; + const cookie = serializeSessionCookie( + rawSession, + isSecure, + new Date(session.expiresAt) + ); + res.appendHeader("Set-Cookie", cookie); + + return response(res, { + data: { valid: true, cookie }, + success: true, + error: false, + message: "Session exchanged successfully", + status: HttpCode.OK + }); + } catch (e) { + console.error(e); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to exchange session" + ) + ); + } +} diff --git a/server/routers/auth/requestEmailVerificationCode.ts b/server/routers/auth/requestEmailVerificationCode.ts index eeabedf2..7358e6ed 100644 --- a/server/routers/auth/requestEmailVerificationCode.ts +++ b/server/routers/auth/requestEmailVerificationCode.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { User } from "@server/db"; import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode"; import config from "@server/lib/config"; diff --git a/server/routers/auth/requestPasswordReset.ts b/server/routers/auth/requestPasswordReset.ts index 62951ab1..52dce2e3 100644 --- a/server/routers/auth/requestPasswordReset.ts +++ b/server/routers/auth/requestPasswordReset.ts @@ -3,7 +3,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db } from "@server/db"; import { passwordResetTokens, users } from "@server/db"; import { eq } from "drizzle-orm"; diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index 753867b6..e6ae4fe4 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; import { encodeHex } from "oslo/encoding"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db } from "@server/db"; import { User, users } from "@server/db"; import { eq, and } from "drizzle-orm"; @@ -113,7 +113,7 @@ export async function requestTotpSecret( const hex = crypto.getRandomValues(new Uint8Array(20)); const secret = encodeHex(hex); const uri = createTOTPKeyURI( - "Pangolin", + config.getRawPrivateConfig().branding?.app_name || "Pangolin", user.email!, hex ); diff --git a/server/routers/auth/resetPassword.ts b/server/routers/auth/resetPassword.ts index 8ae62eb0..05293727 100644 --- a/server/routers/auth/resetPassword.ts +++ b/server/routers/auth/resetPassword.ts @@ -4,7 +4,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db } from "@server/db"; import { passwordResetTokens, users } from "@server/db"; import { eq } from "drizzle-orm"; diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts index 7e131dfd..1e75764b 100644 --- a/server/routers/auth/securityKey.ts +++ b/server/routers/auth/securityKey.ts @@ -6,7 +6,7 @@ import { z } from "zod"; import { db } from "@server/db"; import { User, securityKeys, users, webauthnChallenge } from "@server/db"; import { eq, and, lt } from "drizzle-orm"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import logger from "@server/logger"; import { generateRegistrationOptions, diff --git a/server/routers/auth/setServerAdmin.ts b/server/routers/auth/setServerAdmin.ts index ebb95359..716feca4 100644 --- a/server/routers/auth/setServerAdmin.ts +++ b/server/routers/auth/setServerAdmin.ts @@ -7,7 +7,7 @@ import { generateId } from "@server/auth/sessions/app"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; import { passwordSchema } from "@server/auth/passwordSchema"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db, users, setupTokens } from "@server/db"; import { eq, and } from "drizzle-orm"; import { UserType } from "@server/types/UserTypes"; diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index 09c8db07..0d4f6865 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -21,7 +21,12 @@ import { hashPassword } from "@server/auth/password"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { passwordSchema } from "@server/auth/passwordSchema"; import { UserType } from "@server/types/UserTypes"; +import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; import { build } from "@server/build"; +import resend, { + AudienceIds, + moveEmailToAudience +} from "@server/lib/private/resend"; export const signupBodySchema = z.object({ email: z.string().toLowerCase().email(), @@ -188,6 +193,26 @@ export async function signup( // orgId: null, // }); + if (build == "saas") { + const { success, error, org } = await createUserAccountOrg( + userId, + email + ); + if (!success) { + if (error) { + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, error) + ); + } + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to create user account and organization" + ) + ); + } + } + const token = generateSessionToken(); const sess = await createSession(token, userId); const isSecure = req.protocol === "https"; @@ -198,6 +223,10 @@ export async function signup( ); res.appendHeader("Set-Cookie", cookie); + if (build == "saas") { + moveEmailToAudience(email, AudienceIds.General); + } + if (config.getRawConfig().flags?.require_email_verification) { sendEmailVerificationCode(email, userId); diff --git a/server/routers/auth/verifyEmail.ts b/server/routers/auth/verifyEmail.ts index 97ab540b..010ddf28 100644 --- a/server/routers/auth/verifyEmail.ts +++ b/server/routers/auth/verifyEmail.ts @@ -3,13 +3,15 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db, userOrgs } from "@server/db"; import { User, emailVerificationCodes, users } from "@server/db"; import { eq } from "drizzle-orm"; import { isWithinExpirationDate } from "oslo"; import config from "@server/lib/config"; import logger from "@server/logger"; +import { freeLimitSet, limitsService } from "@server/lib/private/billing"; +import { build } from "@server/build"; export const verifyEmailBody = z .object({ @@ -88,6 +90,19 @@ export async function verifyEmail( ); } + if (build == "saas") { + const orgs = await db + .select() + .from(userOrgs) + .where(eq(userOrgs.userId, user.userId)); + const orgIds = orgs.map((org) => org.orgId); + await Promise.all( + orgIds.map(async (orgId) => { + await limitsService.applyLimitSetToOrg(orgId, freeLimitSet); + }) + ); + } + return response(res, { success: true, error: false, diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 6b45a93e..c44c0c53 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -3,7 +3,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import { db } from "@server/db"; import { twoFactorBackupCodes, User, users } from "@server/db"; import { eq, and } from "drizzle-orm"; diff --git a/server/routers/badger/exchangeSession.ts b/server/routers/badger/exchangeSession.ts index d6f2c7c7..b4b2deea 100644 --- a/server/routers/badger/exchangeSession.ts +++ b/server/routers/badger/exchangeSession.ts @@ -15,7 +15,7 @@ import { import { generateSessionToken, SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app"; import { SESSION_COOKIE_EXPIRES as RESOURCE_SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/resource"; import config from "@server/lib/config"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; const exchangeSessionBodySchema = z.object({ requestToken: z.string(), diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index c482a564..7a0139bb 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -11,9 +11,11 @@ import { getUserOrgRole, getRoleResourceAccess, getUserResourceAccess, - getResourceRules + getResourceRules, + getOrgLoginPage } from "@server/db/queries/verifySessionQueries"; import { + LoginPage, Resource, ResourceAccessToken, ResourcePassword, @@ -32,7 +34,9 @@ import createHttpError from "http-errors"; import NodeCache from "node-cache"; import { z } from "zod"; import { fromError } from "zod-validation-error"; -import { getCountryCodeForIp } from "@server/lib"; +import { getCountryCodeForIp, remoteGetCountryCodeForIp } from "@server/lib/geoip"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; // We'll see if this speeds anything up const cache = new NodeCache({ @@ -196,16 +200,7 @@ export async function verifyResourceSession( return allowed(res); } - let endpoint: string; - if (config.isManagedMode()) { - endpoint = - config.getRawConfig().managed?.redirect_endpoint || - config.getRawConfig().managed?.endpoint || - ""; - } else { - endpoint = config.getRawConfig().app.dashboard_url!; - } - const redirectUrl = `${endpoint}/auth/resource/${encodeURIComponent( + const redirectPath = `/auth/resource/${encodeURIComponent( resource.resourceGuid )}?redirect=${encodeURIComponent(originalRequestURL)}`; @@ -408,7 +403,10 @@ export async function verifyResourceSession( }. IP: ${clientIp}.` ); } - return notAllowed(res, redirectUrl); + + logger.debug(`Redirecting to login at ${redirectPath}`); + + return notAllowed(res, redirectPath, resource.orgId); } catch (e) { console.error(e); return next( @@ -463,7 +461,34 @@ function extractResourceSessionToken( return latest.token; } -function notAllowed(res: Response, redirectUrl?: string) { +async function notAllowed(res: Response, redirectPath?: string, orgId?: string) { + let loginPage: LoginPage | null = null; + if (orgId) { + const { tier } = await getOrgTierData(orgId); // returns null in oss + if (tier === TierId.STANDARD) { + loginPage = await getOrgLoginPage(orgId); + } + } + + let redirectUrl: string | undefined = undefined; + if (redirectPath) { + let endpoint: string; + + if (loginPage && loginPage.domainId && loginPage.fullDomain) { + const secure = config.getRawConfig().app.dashboard_url?.startsWith("https"); + const method = secure ? "https" : "http"; + endpoint = `${method}://${loginPage.fullDomain}`; + } else if (config.isManagedMode()) { + endpoint = + config.getRawConfig().managed?.redirect_endpoint || + config.getRawConfig().managed?.endpoint || + ""; + } else { + endpoint = config.getRawConfig().app.dashboard_url!; + } + redirectUrl = `${endpoint}${redirectPath}`; + } + const data = { data: { valid: false, redirectUrl }, success: true, @@ -762,7 +787,11 @@ async function isIpInGeoIP(ip: string, countryCode: string): Promise { let cachedCountryCode: string | undefined = cache.get(geoIpCacheKey); if (!cachedCountryCode) { - cachedCountryCode = await getCountryCodeForIp(ip); + if (config.isManagedMode()) { + cachedCountryCode = await remoteGetCountryCodeForIp(ip); + } else { + cachedCountryCode = await getCountryCodeForIp(ip); // do it locally + } // Cache for longer since IP geolocation doesn't change frequently cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes } diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index 5adbfa01..f60f14a0 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -17,7 +17,7 @@ import { addPeer as olmAddPeer, deletePeer as olmDeletePeer } from "../olm/peers"; -import { sendToExitNode } from "../../lib/exitNodeComms"; +import { sendToExitNode } from "@server/lib/exitNodes"; const updateClientParamsSchema = z .object({ diff --git a/server/routers/domain/createOrgDomain.ts b/server/routers/domain/createOrgDomain.ts index 08718d44..3744f044 100644 --- a/server/routers/domain/createOrgDomain.ts +++ b/server/routers/domain/createOrgDomain.ts @@ -9,7 +9,9 @@ import { fromError } from "zod-validation-error"; import { subdomainSchema } from "@server/lib/schemas"; import { generateId } from "@server/auth/sessions/app"; import { eq, and } from "drizzle-orm"; -import { isValidDomain } from "@server/lib/validators"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators"; import { build } from "@server/build"; import config from "@server/lib/config"; @@ -98,6 +100,45 @@ export async function createOrgDomain( ); } + if (isSecondLevelDomain(baseDomain) && type == "cname") { + // many providers dont allow cname for this. Lets prevent it for the user for now + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "You cannot create a CNAME record on a root domain. RFC 1912 § 2.4 prohibits CNAME records at the zone apex. Please use a subdomain." + ) + ); + } + + if (build == "saas") { + const usage = await usageService.getUsage(orgId, FeatureId.DOMAINS); + if (!usage) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "No usage data found for this organization" + ) + ); + } + const rejectDomains = await usageService.checkLimitSet( + orgId, + false, + FeatureId.DOMAINS, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectDomains) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Domain limit exceeded. Please upgrade your plan." + ) + ); + } + } + let numOrgDomains: OrgDomains[] | undefined; let aRecords: CreateDomainResponse["aRecords"]; let cnameRecords: CreateDomainResponse["cnameRecords"]; @@ -260,6 +301,14 @@ export async function createOrgDomain( .where(eq(orgDomains.orgId, orgId)); }); + if (numOrgDomains) { + await usageService.updateDaily( + orgId, + FeatureId.DOMAINS, + numOrgDomains.length + ); + } + if (!returned) { return next( createHttpError( diff --git a/server/routers/domain/deleteOrgDomain.ts b/server/routers/domain/deleteOrgDomain.ts index 345dafe7..8932733c 100644 --- a/server/routers/domain/deleteOrgDomain.ts +++ b/server/routers/domain/deleteOrgDomain.ts @@ -7,6 +7,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { and, eq } from "drizzle-orm"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; const paramsSchema = z .object({ @@ -88,6 +90,14 @@ export async function deleteAccountDomain( .where(eq(orgDomains.orgId, orgId)); }); + if (numOrgDomains) { + await usageService.updateDaily( + orgId, + FeatureId.DOMAINS, + numOrgDomains.length + ); + } + return response(res, { data: { success: true }, success: true, diff --git a/server/routers/domain/index.ts b/server/routers/domain/index.ts index c0cafafe..e833e532 100644 --- a/server/routers/domain/index.ts +++ b/server/routers/domain/index.ts @@ -1,4 +1,6 @@ export * from "./listDomains"; export * from "./createOrgDomain"; export * from "./deleteOrgDomain"; +export * from "./privateListDomainNamespaces"; +export * from "./privateCheckDomainNamespaceAvailability"; export * from "./restartOrgDomain"; \ No newline at end of file diff --git a/server/routers/domain/privateCheckDomainNamespaceAvailability.ts b/server/routers/domain/privateCheckDomainNamespaceAvailability.ts new file mode 100644 index 00000000..f1a4e103 --- /dev/null +++ b/server/routers/domain/privateCheckDomainNamespaceAvailability.ts @@ -0,0 +1,127 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +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 { db, domainNamespaces, resources } from "@server/db"; +import { inArray } from "drizzle-orm"; + +const paramsSchema = z.object({}).strict(); + +const querySchema = z + .object({ + subdomain: z.string() + }) + .strict(); + +export type CheckDomainAvailabilityResponse = { + available: boolean; + options: { + domainNamespaceId: string; + domainId: string; + fullDomain: string; + }[]; +}; + +registry.registerPath({ + method: "get", + path: "/domain/check-namespace-availability", + description: "Check if a domain namespace is available based on subdomain", + tags: [OpenAPITags.Domain], + request: { + params: paramsSchema, + query: querySchema + }, + responses: {} +}); + +export async function checkDomainNamespaceAvailability( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + const { subdomain } = parsedQuery.data; + + const namespaces = await db.select().from(domainNamespaces); + let possibleDomains = namespaces.map((ns) => { + const desired = `${subdomain}.${ns.domainNamespaceId}`; + return { + fullDomain: desired, + domainId: ns.domainId, + domainNamespaceId: ns.domainNamespaceId + }; + }); + + if (!possibleDomains.length) { + return response(res, { + data: { + available: false, + options: [] + }, + success: true, + error: false, + message: "No domain namespaces available", + status: HttpCode.OK + }); + } + + const existingResources = await db + .select() + .from(resources) + .where( + inArray( + resources.fullDomain, + possibleDomains.map((d) => d.fullDomain) + ) + ); + + possibleDomains = possibleDomains.filter( + (domain) => + !existingResources.some( + (resource) => resource.fullDomain === domain.fullDomain + ) + ); + + return response(res, { + data: { + available: possibleDomains.length > 0, + options: possibleDomains + }, + success: true, + error: false, + message: "Domain namespaces checked successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/domain/privateListDomainNamespaces.ts b/server/routers/domain/privateListDomainNamespaces.ts new file mode 100644 index 00000000..10bcc91b --- /dev/null +++ b/server/routers/domain/privateListDomainNamespaces.ts @@ -0,0 +1,130 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, domainNamespaces } from "@server/db"; +import { domains } from "@server/db"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import { eq, sql } from "drizzle-orm"; +import logger from "@server/logger"; +import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; + +const paramsSchema = z.object({}).strict(); + +const querySchema = z + .object({ + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.number().int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.number().int().nonnegative()) + }) + .strict(); + +async function query(limit: number, offset: number) { + const res = await db + .select({ + domainNamespaceId: domainNamespaces.domainNamespaceId, + domainId: domainNamespaces.domainId + }) + .from(domainNamespaces) + .innerJoin( + domains, + eq(domains.domainId, domainNamespaces.domainNamespaceId) + ) + .limit(limit) + .offset(offset); + return res; +} + +export type ListDomainNamespacesResponse = { + domainNamespaces: NonNullable>>; + pagination: { total: number; limit: number; offset: number }; +}; + +registry.registerPath({ + method: "get", + path: "/domains/namepaces", + description: "List all domain namespaces in the system", + tags: [OpenAPITags.Domain], + request: { + query: querySchema + }, + responses: {} +}); + +export async function listDomainNamespaces( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + const { limit, offset } = parsedQuery.data; + + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const domainNamespacesList = await query(limit, offset); + + const [{ count }] = await db + .select({ count: sql`count(*)` }) + .from(domainNamespaces); + + return response(res, { + data: { + domainNamespaces: domainNamespacesList, + pagination: { + total: count, + limit, + offset + } + }, + success: true, + error: false, + message: "Namespaces retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/external.ts b/server/routers/external.ts index 08b3c119..d6fa4a16 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -38,13 +38,25 @@ import { verifyUserIsOrgOwner, verifySiteResourceAccess } from "@server/middlewares"; -import { createStore } from "@server/lib/rateLimitStore"; +import { + verifyCertificateAccess, + verifyRemoteExitNodeAccess, + verifyIdpAccess, + verifyLoginPageAccess +} from "@server/middlewares/private"; +import { createStore } from "@server/lib/private/rateLimitStore"; import { ActionsEnum } from "@server/auth/actions"; import { createNewt, getNewtToken } from "./newt"; import { getOlmToken } from "./olm"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; +import * as certificates from "./private/certificates"; +import * as billing from "@server/routers/private/billing"; +import { quickStart } from "./auth/privateQuickStart"; import { build } from "@server/build"; +import * as remoteExitNode from "@server/routers/private/remoteExitNode"; +import * as loginPage from "@server/routers/private/loginPage"; +import * as orgIdp from "@server/routers/private/orgIdp"; // Root routes export const unauthenticated = Router(); @@ -53,6 +65,45 @@ unauthenticated.get("/", (_, res) => { res.status(HttpCode.OK).json({ message: "Healthy" }); }); +if (build === "saas") { + unauthenticated.post( + "/quick-start", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + keyGenerator: (req) => req.path, + handler: (req, res, next) => { + const message = `We're too busy right now. Please try again later.`; + return next( + createHttpError(HttpCode.TOO_MANY_REQUESTS, message) + ); + }, + store: createStore() + }), + quickStart + ); +} + +if (build !== "oss") { + unauthenticated.post( + "/remote-exit-node/quick-start", + rateLimit({ + windowMs: 60 * 60 * 1000, + max: 5, + keyGenerator: (req) => + `${req.path}:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only create 5 remote exit nodes every hour. Please try again later.`; + return next( + createHttpError(HttpCode.TOO_MANY_REQUESTS, message) + ); + }, + store: createStore() + }), + remoteExitNode.quickStartRemoteExitNode + ); +} + // Authenticated Root routes export const authenticated = Router(); authenticated.use(verifySessionUserMiddleware); @@ -540,7 +591,10 @@ authenticated.post( ); authenticated.post(`/supporter-key/hide`, supporterKey.hideSupporterKey); -unauthenticated.get("/resource/:resourceGuid/auth", resource.getResourceAuthInfo); +unauthenticated.get( + "/resource/:resourceGuid/auth", + resource.getResourceAuthInfo +); // authenticated.get( // "/role/:roleId/resources", @@ -666,6 +720,46 @@ authenticated.post( idp.updateOidcIdp ); +if (build !== "oss") { + authenticated.put( + "/org/:orgId/idp/oidc", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createIdp), + orgIdp.createOrgOidcIdp + ); + + authenticated.post( + "/org/:orgId/idp/:idpId/oidc", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.updateIdp), + orgIdp.updateOrgOidcIdp + ); + + authenticated.delete( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.deleteIdp), + idp.deleteIdp + ); + + authenticated.get( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.getIdp), + orgIdp.getOrgIdp + ); + + authenticated.get( + "/org/:orgId/idp", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listIdps), + orgIdp.listOrgIdps + ); +} + authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp); authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); @@ -694,6 +788,9 @@ authenticated.get( idp.listIdpOrgPolicies ); +if (build !== "oss") { + authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids +} authenticated.get("/idp", idp.listIdps); // anyone can see this; it's just a list of idp names and ids authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); @@ -826,6 +923,126 @@ authenticated.delete( domain.deleteAccountDomain ); +if (build !== "oss") { + authenticated.get( + "/org/:orgId/certificate/:domainId/:domain", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.getCertificate), + certificates.getCertificate + ); + + authenticated.post( + "/org/:orgId/certificate/:certId/restart", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.restartCertificate), + certificates.restartCertificate + ); + + authenticated.post( + "/org/:orgId/billing/create-checkout-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createCheckoutSession + ); + + authenticated.post( + "/org/:orgId/billing/create-portal-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createPortalSession + ); + + authenticated.get( + "/org/:orgId/billing/subscription", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgSubscription + ); + + authenticated.get( + "/org/:orgId/billing/usage", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgUsage + ); + + authenticated.get("/domain/namespaces", domain.listDomainNamespaces); + + authenticated.get( + "/domain/check-namespace-availability", + domain.checkDomainNamespaceAvailability + ); + + authenticated.put( + "/org/:orgId/remote-exit-node", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.createRemoteExitNode + ); + + authenticated.get( + "/org/:orgId/remote-exit-nodes", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listRemoteExitNode), + remoteExitNode.listRemoteExitNodes + ); + + authenticated.get( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.getRemoteExitNode), + remoteExitNode.getRemoteExitNode + ); + + authenticated.get( + "/org/:orgId/pick-remote-exit-node-defaults", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.pickRemoteExitNodeDefaults + ); + + authenticated.delete( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), + remoteExitNode.deleteRemoteExitNode + ); + + authenticated.put( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createLoginPage), + loginPage.createLoginPage + ); + + authenticated.post( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.updateLoginPage), + loginPage.updateLoginPage + ); + + authenticated.delete( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.deleteLoginPage), + loginPage.deleteLoginPage + ); + + authenticated.get( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.getLoginPage), + loginPage.getLoginPage + ); +} + // Auth routes export const authRouter = Router(); unauthenticated.use("/auth", authRouter); @@ -833,7 +1050,8 @@ authRouter.use( rateLimit({ windowMs: config.getRawConfig().rate_limits.auth.window_minutes, max: config.getRawConfig().rate_limits.auth.max_requests, - keyGenerator: (req) => `authRouterGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`, + keyGenerator: (req) => + `authRouterGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`, handler: (req, res, next) => { const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.auth.max_requests} requests every ${config.getRawConfig().rate_limits.auth.window_minutes} minute(s).`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -847,7 +1065,8 @@ authRouter.put( rateLimit({ windowMs: 15 * 60 * 1000, max: 15, - keyGenerator: (req) => `signup:${ipKeyGenerator(req.ip || "")}:${req.body.email}`, + keyGenerator: (req) => + `signup:${ipKeyGenerator(req.ip || "")}:${req.body.email}`, handler: (req, res, next) => { const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -861,7 +1080,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 15, - keyGenerator: (req) => `login:${req.body.email || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `login:${req.body.email || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only log in ${15} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -876,7 +1096,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 900, - keyGenerator: (req) => `newtGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `newtGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only request a Newt token ${900} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -890,7 +1111,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 900, - keyGenerator: (req) => `olmGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `olmGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -900,6 +1122,26 @@ authRouter.post( getOlmToken ); +if (build !== "oss") { + authRouter.post( + "/remoteExitNode/get-token", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 900, + keyGenerator: (req) => + `remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`; + return next( + createHttpError(HttpCode.TOO_MANY_REQUESTS, message) + ); + }, + store: createStore() + }), + remoteExitNode.getRemoteExitNodeToken + ); +} + authRouter.post( "/2fa/enable", rateLimit({ @@ -938,7 +1180,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 15, - keyGenerator: (req) => `signup:${req.user?.userId || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `signup:${req.user?.userId || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only disable 2FA ${15} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -952,7 +1195,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 15, - keyGenerator: (req) => `signup:${req.body.email || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `signup:${req.body.email || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -1007,7 +1251,8 @@ authRouter.post( rateLimit({ windowMs: 15 * 60 * 1000, max: 15, - keyGenerator: (req) => `resetPassword:${req.body.email || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `resetPassword:${req.body.email || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); @@ -1064,6 +1309,26 @@ authRouter.post( resource.authWithWhitelist ); +if (build !== "oss") { + authRouter.post( + "/transfer-session-token", + rateLimit({ + windowMs: 1 * 60 * 1000, + max: 60, + keyGenerator: (req) => + `transferSessionToken:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`; + return next( + createHttpError(HttpCode.TOO_MANY_REQUESTS, message) + ); + }, + store: createStore() + }), + auth.transferSession + ); +} + authRouter.post( "/resource/:resourceId/access-token", resource.authWithAccessToken @@ -1129,7 +1394,8 @@ authRouter.delete( rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 20, // Allow 10 authentication attempts per 15 minutes per IP - keyGenerator: (req) => `securityKeyAuth:${req.user?.userId || ipKeyGenerator(req.ip || "")}`, + keyGenerator: (req) => + `securityKeyAuth:${req.user?.userId || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only delete a security key ${10} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index 71d1a45e..afae4009 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -13,7 +13,7 @@ import { fromError } from "zod-validation-error"; import { getAllowedIps } from "../target/helpers"; import { proxyToRemote } from "@server/lib/remoteProxy"; import { getNextAvailableSubnet } from "@server/lib/exitNodes"; -import { createExitNode } from "./createExitNode"; +import { createExitNode } from "./privateCreateExitNode"; // Define Zod schema for request validation const getConfigSchema = z.object({ diff --git a/server/routers/gerbil/getResolvedHostname.ts b/server/routers/gerbil/getResolvedHostname.ts index da2ab39a..17067c55 100644 --- a/server/routers/gerbil/getResolvedHostname.ts +++ b/server/routers/gerbil/getResolvedHostname.ts @@ -4,6 +4,9 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { resolveExitNodes } from "@server/lib/exitNodes"; +import config from "@server/lib/config"; +import { build } from "@server/build"; // Define Zod schema for request validation const getResolvedHostnameSchema = z.object({ @@ -17,22 +20,42 @@ export async function getResolvedHostname( next: NextFunction ): Promise { try { - // Validate request parameters - const parsedParams = getResolvedHostnameSchema.safeParse( - req.body - ); - if (!parsedParams.success) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - fromError(parsedParams.error).toString() - ) + let endpoints: string[] = []; // always route locally + + if (build != "oss") { + // Validate request parameters + const parsedParams = getResolvedHostnameSchema.safeParse(req.body); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { hostname, publicKey } = parsedParams.data; + + const baseDomain = config.getRawPrivateConfig().app.base_domain; + + // if the hostname ends with the base domain then send back a empty array + if (baseDomain && hostname.endsWith(baseDomain)) { + return res.status(HttpCode.OK).send({ + endpoints: [] // this should force to route locally + }); + } + + const resourceExitNodes = await resolveExitNodes( + hostname, + publicKey ); + + endpoints = resourceExitNodes.map((node) => node.endpoint); } // return the endpoints return res.status(HttpCode.OK).send({ - endpoints: [] // ALWAYS ROUTE LOCALLY + endpoints }); } catch (error) { logger.error(error); diff --git a/server/routers/gerbil/peers.ts b/server/routers/gerbil/peers.ts index 51a338a7..1cdc9184 100644 --- a/server/routers/gerbil/peers.ts +++ b/server/routers/gerbil/peers.ts @@ -2,7 +2,7 @@ import logger from "@server/logger"; import { db } from "@server/db"; import { exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToExitNode } from "../../lib/exitNodeComms"; +import { sendToExitNode } from "@server/lib/exitNodes"; export async function addPeer( exitNodeId: number, diff --git a/server/routers/gerbil/privateCreateExitNode.ts b/server/routers/gerbil/privateCreateExitNode.ts new file mode 100644 index 00000000..9c2a104d --- /dev/null +++ b/server/routers/gerbil/privateCreateExitNode.ts @@ -0,0 +1,67 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { db, ExitNode, exitNodes } from "@server/db"; +import { getUniqueExitNodeEndpointName } from "@server/db/names"; +import config from "@server/lib/config"; +import { getNextAvailableSubnet } from "@server/lib/exitNodes"; +import logger from "@server/logger"; +import { eq } from "drizzle-orm"; + +export async function createExitNode( + publicKey: string, + reachableAt: string | undefined +) { + // Fetch exit node + const [exitNodeQuery] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.publicKey, publicKey)); + let exitNode: ExitNode; + if (!exitNodeQuery) { + const address = await getNextAvailableSubnet(); + // TODO: eventually we will want to get the next available port so that we can multiple exit nodes + // const listenPort = await getNextAvailablePort(); + const listenPort = config.getRawConfig().gerbil.start_port; + let subEndpoint = ""; + if (config.getRawConfig().gerbil.use_subdomain) { + subEndpoint = await getUniqueExitNodeEndpointName(); + } + + const exitNodeName = + config.getRawConfig().gerbil.exit_node_name || + `Exit Node ${publicKey.slice(0, 8)}`; + + // create a new exit node + [exitNode] = await db + .insert(exitNodes) + .values({ + publicKey, + endpoint: `${subEndpoint}${subEndpoint != "" ? "." : ""}${config.getRawConfig().gerbil.base_endpoint}`, + address, + listenPort, + reachableAt, + name: exitNodeName + }) + .returning() + .execute(); + + logger.info( + `Created new exit node ${exitNode.name} with address ${exitNode.address} and port ${exitNode.listenPort}` + ); + } else { + exitNode = exitNodeQuery; + } + + return exitNode; +} diff --git a/server/routers/gerbil/privateReceiveBandwidth.ts b/server/routers/gerbil/privateReceiveBandwidth.ts new file mode 100644 index 00000000..de0b2d2b --- /dev/null +++ b/server/routers/gerbil/privateReceiveBandwidth.ts @@ -0,0 +1,13 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index fb7723ee..d10141b9 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -6,7 +6,10 @@ import logger from "@server/logger"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing/features"; import { checkExitNodeOrg } from "@server/lib/exitNodes"; +import { build } from "@server/build"; // Track sites that are already offline to avoid unnecessary queries const offlineSites = new Set(); @@ -29,7 +32,7 @@ export const receiveBandwidth = async ( throw new Error("Invalid bandwidth data"); } - await updateSiteBandwidth(bandwidthData); + await updateSiteBandwidth(bandwidthData, build == "saas"); // we are checking the usage on saas only return response(res, { data: {}, @@ -51,6 +54,7 @@ export const receiveBandwidth = async ( export async function updateSiteBandwidth( bandwidthData: PeerBandwidth[], + calcUsageAndLimits: boolean, exitNodeId?: number ) { const currentTime = new Date(); @@ -89,18 +93,23 @@ export async function updateSiteBandwidth( lastBandwidthUpdate: sites.lastBandwidthUpdate }); - if (exitNodeId) { - if (await checkExitNodeOrg(exitNodeId, updatedSite.orgId)) { - // not allowed - logger.warn( - `Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}` - ); - // THIS SHOULD TRIGGER THE TRANSACTION TO FAIL? - throw new Error("Exit node not allowed"); - } - } - if (updatedSite) { + if (exitNodeId) { + if ( + await checkExitNodeOrg( + exitNodeId, + updatedSite.orgId + ) + ) { + // not allowed + logger.warn( + `Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}` + ); + // THIS SHOULD TRIGGER THE TRANSACTION TO FAIL? + throw new Error("Exit node not allowed"); + } + } + updatedSites.push({ ...updatedSite, peer }); } } @@ -116,6 +125,74 @@ export async function updateSiteBandwidth( const currentOrgUptime = orgUptimeMap.get(site.orgId) || 0; orgUptimeMap.set(site.orgId, currentOrgUptime + 10 / 60); // Store in minutes and jut add 10 seconds } + + if (calcUsageAndLimits) { + // REMOTE EXIT NODES DO NOT COUNT TOWARDS USAGE + // Process all usage updates sequentially by organization to reduce deadlock risk + const allOrgIds = new Set([...orgUsageMap.keys(), ...orgUptimeMap.keys()]); + + for (const orgId of allOrgIds) { + try { + // Process bandwidth usage for this org + const totalBandwidth = orgUsageMap.get(orgId); + if (totalBandwidth) { + const bandwidthUsage = await usageService.add( + orgId, + FeatureId.EGRESS_DATA_MB, + totalBandwidth, + trx + ); + if (bandwidthUsage) { + usageService + .checkLimitSet( + orgId, + true, + FeatureId.EGRESS_DATA_MB, + bandwidthUsage + ) + .catch((error: any) => { + logger.error( + `Error checking bandwidth limits for org ${orgId}:`, + error + ); + }); + } + } + + // Process uptime usage for this org + const totalUptime = orgUptimeMap.get(orgId); + if (totalUptime) { + const uptimeUsage = await usageService.add( + orgId, + FeatureId.SITE_UPTIME, + totalUptime, + trx + ); + if (uptimeUsage) { + usageService + .checkLimitSet( + orgId, + true, + FeatureId.SITE_UPTIME, + uptimeUsage + ) + .catch((error: any) => { + logger.error( + `Error checking uptime limits for org ${orgId}:`, + error + ); + }); + } + } + } catch (error) { + logger.error( + `Error processing usage for org ${orgId}:`, + error + ); + // Don't break the loop, continue with other orgs + } + } + } } // Handle sites that reported zero bandwidth but need online status updated @@ -161,7 +238,7 @@ export async function updateSiteBandwidth( .where(eq(sites.siteId, site.siteId)) .returning(); - if (exitNodeId) { + if (updatedSite && exitNodeId) { if ( await checkExitNodeOrg( exitNodeId, diff --git a/server/routers/gerbil/updateHolePunch.ts b/server/routers/gerbil/updateHolePunch.ts index 1662e420..65217178 100644 --- a/server/routers/gerbil/updateHolePunch.ts +++ b/server/routers/gerbil/updateHolePunch.ts @@ -105,7 +105,7 @@ export async function updateHolePunch( destinations: destinations }); } catch (error) { - logger.error(error); + // logger.error(error); // FIX THIS return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, @@ -122,7 +122,8 @@ export async function updateAndGenerateEndpointDestinations( port: number, timestamp: number, token: string, - exitNode: ExitNode + exitNode: ExitNode, + checkOrg = false ) { let currentSiteId: number | undefined; const destinations: PeerDestination[] = []; @@ -158,7 +159,7 @@ export async function updateAndGenerateEndpointDestinations( .where(eq(clients.clientId, olm.clientId)) .returning(); - if (await checkExitNodeOrg(exitNode.exitNodeId, client.orgId)) { + if (await checkExitNodeOrg(exitNode.exitNodeId, client.orgId) && checkOrg) { // not allowed logger.warn( `Exit node ${exitNode.exitNodeId} is not allowed for org ${client.orgId}` @@ -253,7 +254,7 @@ export async function updateAndGenerateEndpointDestinations( .where(eq(sites.siteId, newt.siteId)) .limit(1); - if (await checkExitNodeOrg(exitNode.exitNodeId, site.orgId)) { + if (await checkExitNodeOrg(exitNode.exitNodeId, site.orgId) && checkOrg) { // not allowed logger.warn( `Exit node ${exitNode.exitNodeId} is not allowed for org ${site.orgId}` diff --git a/server/routers/idp/createOidcIdp.ts b/server/routers/idp/createOidcIdp.ts index 6078f5aa..223a08b8 100644 --- a/server/routers/idp/createOidcIdp.ts +++ b/server/routers/idp/createOidcIdp.ts @@ -112,7 +112,7 @@ export async function createOidcIdp( }); }); - const redirectUrl = generateOidcRedirectUrl(idpId as number); + const redirectUrl = await generateOidcRedirectUrl(idpId as number); return response(res, { data: { diff --git a/server/routers/idp/deleteIdp.ts b/server/routers/idp/deleteIdp.ts index e862c81c..58b231b7 100644 --- a/server/routers/idp/deleteIdp.ts +++ b/server/routers/idp/deleteIdp.ts @@ -12,6 +12,7 @@ import { OpenAPITags, registry } from "@server/openApi"; const paramsSchema = z .object({ + orgId: z.string().optional(), // Optional; used with org idp in saas idpId: z.coerce.number() }) .strict(); diff --git a/server/routers/idp/generateOidcUrl.ts b/server/routers/idp/generateOidcUrl.ts index c507198a..90144816 100644 --- a/server/routers/idp/generateOidcUrl.ts +++ b/server/routers/idp/generateOidcUrl.ts @@ -10,10 +10,12 @@ import { idp, idpOidcConfig, idpOrg } from "@server/db"; import { and, eq } from "drizzle-orm"; import * as arctic from "arctic"; import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; -import cookie from "cookie"; import jsonwebtoken from "jsonwebtoken"; import config from "@server/lib/config"; import { decrypt } from "@server/lib/crypto"; +import { build } from "@server/build"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; const paramsSchema = z .object({ @@ -27,6 +29,10 @@ const bodySchema = z }) .strict(); +const querySchema = z.object({ + orgId: z.string().optional() // check what actuall calls it +}); + const ensureTrailingSlash = (url: string): string => { return url; }; @@ -65,6 +71,18 @@ export async function generateOidcUrl( const { redirectUrl: postAuthRedirectUrl } = parsedBody.data; + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + + const { orgId } = parsedQuery.data; + const [existingIdp] = await db .select() .from(idp) @@ -80,6 +98,36 @@ export async function generateOidcUrl( ); } + if (orgId) { + const [idpOrgLink] = await db + .select() + .from(idpOrg) + .where(and(eq(idpOrg.idpId, idpId), eq(idpOrg.orgId, orgId))) + .limit(1); + + if (!idpOrgLink) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "IdP not found for the organization" + ) + ); + } + + if (build === "saas") { + const { tier } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + } + const parsedScopes = existingIdp.idpOidcConfig.scopes .split(" ") .map((scope) => { @@ -100,7 +148,12 @@ export async function generateOidcUrl( key ); - const redirectUrl = generateOidcRedirectUrl(idpId); + const redirectUrl = await generateOidcRedirectUrl(idpId, orgId); + logger.debug("OIDC client info", { + decryptedClientId, + decryptedClientSecret, + redirectUrl + }); const client = new arctic.OAuth2Client( decryptedClientId, decryptedClientSecret, @@ -116,7 +169,6 @@ export async function generateOidcUrl( codeVerifier, parsedScopes ); - logger.debug("Generated OIDC URL", { url }); const stateJwt = jsonwebtoken.sign( { diff --git a/server/routers/idp/index.ts b/server/routers/idp/index.ts index f0dcf02e..81cec8d1 100644 --- a/server/routers/idp/index.ts +++ b/server/routers/idp/index.ts @@ -8,4 +8,4 @@ export * from "./getIdp"; export * from "./createIdpOrgPolicy"; export * from "./deleteIdpOrgPolicy"; export * from "./listIdpOrgPolicies"; -export * from "./updateIdpOrgPolicy"; +export * from "./updateIdpOrgPolicy"; \ No newline at end of file diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 46baa517..fec21e41 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -30,6 +30,8 @@ import { } from "@server/auth/sessions/app"; import { decrypt } from "@server/lib/crypto"; import { UserType } from "@server/types/UserTypes"; +import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/private/billing/usageService"; const ensureTrailingSlash = (url: string): string => { return url; @@ -47,6 +49,10 @@ const bodySchema = z.object({ storedState: z.string().nonempty() }); +const querySchema = z.object({ + loginPageId: z.coerce.number().optional() +}); + export type ValidateOidcUrlCallbackResponse = { redirectUrl: string; }; @@ -79,6 +85,18 @@ export async function validateOidcCallback( ); } + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + + const { loginPageId } = parsedQuery.data; + const { storedState, code, state: expectedState } = parsedBody.data; const [existingIdp] = await db @@ -107,7 +125,11 @@ export async function validateOidcCallback( key ); - const redirectUrl = generateOidcRedirectUrl(existingIdp.idp.idpId); + const redirectUrl = await generateOidcRedirectUrl( + existingIdp.idp.idpId, + undefined, + loginPageId + ); const client = new arctic.OAuth2Client( decryptedClientId, decryptedClientSecret, @@ -380,12 +402,14 @@ export async function validateOidcCallback( } // Update roles for existing auto-provisioned orgs where the role has changed - const orgsToUpdate = autoProvisionedOrgs.filter((currentOrg) => { - const newOrg = userOrgInfo.find( - (newOrg) => newOrg.orgId === currentOrg.orgId - ); - return newOrg && newOrg.roleId !== currentOrg.roleId; - }); + const orgsToUpdate = autoProvisionedOrgs.filter( + (currentOrg) => { + const newOrg = userOrgInfo.find( + (newOrg) => newOrg.orgId === currentOrg.orgId + ); + return newOrg && newOrg.roleId !== currentOrg.roleId; + } + ); if (orgsToUpdate.length > 0) { for (const org of orgsToUpdate) { @@ -441,6 +465,14 @@ export async function validateOidcCallback( } }); + for (const orgCount of orgUserCounts) { + await usageService.updateDaily( + orgCount.orgId, + FeatureId.USERS, + orgCount.userCount + ); + } + const token = generateSessionToken(); const sess = await createSession(token, existingUserId!); const isSecure = req.protocol === "https"; diff --git a/server/routers/integration.ts b/server/routers/integration.ts index 6a43aaa7..edad73b9 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -30,6 +30,7 @@ import { import HttpCode from "@server/types/HttpCode"; import { Router } from "express"; import { ActionsEnum } from "@server/auth/actions"; +import { build } from "@server/build"; export const unauthenticated = Router(); @@ -597,6 +598,15 @@ authenticated.get( idp.listIdpOrgPolicies ); +if (build == "saas") { + authenticated.post( + `/org/:orgId/send-usage-notification`, + verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine + verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), + org.sendUsageNotification + ); +} + authenticated.get( "/org/:orgId/pick-client-defaults", verifyClientsEnabled, diff --git a/server/routers/internal.ts b/server/routers/internal.ts index b961ef6f..e4525118 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -7,6 +7,7 @@ import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; import * as license from "@server/routers/license"; import * as idp from "@server/routers/idp"; +import * as loginPage from "@server/routers/private/loginPage"; import { proxyToRemote } from "@server/lib/remoteProxy"; import config from "@server/lib/config"; import HttpCode from "@server/types/HttpCode"; @@ -14,6 +15,9 @@ import { verifyResourceAccess, verifySessionUserMiddleware } from "@server/middlewares"; +import { build } from "@server/build"; +import * as billing from "@server/routers/private/billing"; +import * as orgIdp from "@server/routers/private/orgIdp"; // Root routes const internalRouter = Router(); @@ -47,6 +51,12 @@ internalRouter.get("/idp", idp.listIdps); internalRouter.get("/idp/:idpId", idp.getIdp); +if (build !== "oss") { + internalRouter.get("/org/:orgId/idp", orgIdp.listOrgIdps); + + internalRouter.get("/org/:orgId/billing/tier", billing.getOrgTier); +} + // Gerbil routes const gerbilRouter = Router(); internalRouter.use("/gerbil", gerbilRouter); @@ -98,4 +108,14 @@ if (config.isManagedMode()) { badgerRouter.post("/exchange-session", badger.exchangeSession); } +if (build !== "oss") { + internalRouter.get("/login-page", loginPage.loadLoginPage); + + internalRouter.post( + "/get-session-transfer-token", + verifySessionUserMiddleware, + auth.getSessionTransferToken + ); +} + export default internalRouter; diff --git a/server/routers/license/activateLicense.ts b/server/routers/license/activateLicense.ts index 3826277c..832bc19d 100644 --- a/server/routers/license/activateLicense.ts +++ b/server/routers/license/activateLicense.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import license, { LicenseStatus } from "@server/license/license"; import { z } from "zod"; import { fromError } from "zod-validation-error"; diff --git a/server/routers/license/deleteLicenseKey.ts b/server/routers/license/deleteLicenseKey.ts index 2663308e..37b74fee 100644 --- a/server/routers/license/deleteLicenseKey.ts +++ b/server/routers/license/deleteLicenseKey.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import { db } from "@server/db"; diff --git a/server/routers/license/getLicenseStatus.ts b/server/routers/license/getLicenseStatus.ts index e46b4caa..e4f28882 100644 --- a/server/routers/license/getLicenseStatus.ts +++ b/server/routers/license/getLicenseStatus.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import license, { LicenseStatus } from "@server/license/license"; export type GetLicenseStatusResponse = LicenseStatus; diff --git a/server/routers/license/listLicenseKeys.ts b/server/routers/license/listLicenseKeys.ts index 915690df..d106abd7 100644 --- a/server/routers/license/listLicenseKeys.ts +++ b/server/routers/license/listLicenseKeys.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import license, { LicenseKeyCache } from "@server/license/license"; export type ListLicenseKeysResponse = LicenseKeyCache[]; diff --git a/server/routers/license/recheckStatus.ts b/server/routers/license/recheckStatus.ts index d2ab7939..cd4bf779 100644 --- a/server/routers/license/recheckStatus.ts +++ b/server/routers/license/recheckStatus.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import license, { LicenseStatus } from "@server/license/license"; export type RecheckStatusResponse = LicenseStatus; diff --git a/server/routers/newt/handleGetConfigMessage.ts b/server/routers/newt/handleGetConfigMessage.ts index b6206064..2b65fd06 100644 --- a/server/routers/newt/handleGetConfigMessage.ts +++ b/server/routers/newt/handleGetConfigMessage.ts @@ -14,7 +14,7 @@ import { import { clients, clientSites, Newt, sites } from "@server/db"; import { eq, and, inArray } from "drizzle-orm"; import { updatePeer } from "../olm/peers"; -import { sendToExitNode } from "../../lib/exitNodeComms"; +import { sendToExitNode } from "@server/lib/exitNodes"; const inputSchema = z.object({ publicKey: z.string(), @@ -66,7 +66,7 @@ export const handleGetConfigMessage: MessageHandler = async (context) => { // we need to wait for hole punch success if (!existingSite.endpoint) { - logger.warn(`Site ${existingSite.siteId} has no endpoint, skipping`); + logger.debug(`In newt get config: existing site ${existingSite.siteId} has no endpoint, skipping`); return; } @@ -74,12 +74,12 @@ export const handleGetConfigMessage: MessageHandler = async (context) => { // TODO: somehow we should make sure a recent hole punch has happened if this occurs (hole punch could be from the last restart if done quickly) } - if (existingSite.lastHolePunch && now - existingSite.lastHolePunch > 6) { - logger.warn( - `Site ${existingSite.siteId} last hole punch is too old, skipping` - ); - return; - } + // if (existingSite.lastHolePunch && now - existingSite.lastHolePunch > 6) { + // logger.warn( + // `Site ${existingSite.siteId} last hole punch is too old, skipping` + // ); + // return; + // } // update the endpoint and the public key const [site] = await db @@ -176,7 +176,7 @@ export const handleGetConfigMessage: MessageHandler = async (context) => { if (!endpoint) { logger.warn( - `Site ${site.siteId} has no endpoint, skipping` + `In Newt get config: Peer site ${site.siteId} has no endpoint, skipping` ); return null; } diff --git a/server/routers/newt/handleNewtPingRequestMessage.ts b/server/routers/newt/handleNewtPingRequestMessage.ts index f93862f6..aeb7a155 100644 --- a/server/routers/newt/handleNewtPingRequestMessage.ts +++ b/server/routers/newt/handleNewtPingRequestMessage.ts @@ -29,7 +29,14 @@ export const handleNewtPingRequestMessage: MessageHandler = async (context) => { .where(eq(sites.siteId, newt.siteId)) .limit(1); - const exitNodesList = await listExitNodes(site.orgId, true); // filter for only the online ones + if (!site || !site.orgId) { + logger.warn("Site not found"); + return; + } + + const { noCloud } = message.data; + + const exitNodesList = await listExitNodes(site.orgId, true, noCloud || false); // filter for only the online ones let lastExitNodeId = null; if (newt.siteId) { diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index eef78765..51a1ab05 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -1,6 +1,7 @@ -import { db, newts } from "@server/db"; +import { db, exitNodeOrgs, newts } from "@server/db"; import { MessageHandler } from "../ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; +import { targetHealthCheck } from "@server/db"; import { eq, and, sql, inArray } from "drizzle-orm"; import { addPeer, deletePeer } from "../gerbil/peers"; import logger from "@server/logger"; @@ -9,7 +10,12 @@ import { findNextAvailableCidr, getNextAvailableClientSubnet } from "@server/lib/ip"; -import { selectBestExitNode, verifyExitNodeOrgAccess } from "@server/lib/exitNodes"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { + selectBestExitNode, + verifyExitNodeOrgAccess +} from "@server/lib/exitNodes"; import { fetchContainers } from "./dockerSocket"; export type ExitNodePingResult = { @@ -22,6 +28,8 @@ export type ExitNodePingResult = { wasPreviouslyConnected: boolean; }; +let numTimesLimitExceededForId: Record = {}; + export const handleNewtRegisterMessage: MessageHandler = async (context) => { const { message, client, sendToClient } = context; const newt = client as Newt; @@ -86,13 +94,52 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { fetchContainers(newt.newtId); } + const rejectSiteUptime = await usageService.checkLimitSet( + oldSite.orgId, + false, + FeatureId.SITE_UPTIME + ); + const rejectEgressDataMb = await usageService.checkLimitSet( + oldSite.orgId, + false, + FeatureId.EGRESS_DATA_MB + ); + + // Do we need to check the users and domains daily limits here? + // const rejectUsers = await usageService.checkLimitSet(oldSite.orgId, false, FeatureId.USERS); + // const rejectDomains = await usageService.checkLimitSet(oldSite.orgId, false, FeatureId.DOMAINS); + + // if (rejectEgressDataMb || rejectSiteUptime || rejectUsers || rejectDomains) { + if (rejectEgressDataMb || rejectSiteUptime) { + logger.info( + `Usage limits exceeded for org ${oldSite.orgId}. Rejecting newt registration.` + ); + + // PREVENT FURTHER REGISTRATION ATTEMPTS SO WE DON'T SPAM + + // Increment the limit exceeded count for this site + numTimesLimitExceededForId[newt.newtId] = + (numTimesLimitExceededForId[newt.newtId] || 0) + 1; + + if (numTimesLimitExceededForId[newt.newtId] > 15) { + logger.debug( + `Newt ${newt.newtId} has exceeded usage limits 15 times. Terminating...` + ); + } + + return; + } + let siteSubnet = oldSite.subnet; let exitNodeIdToQuery = oldSite.exitNodeId; if (exitNodeId && (oldSite.exitNodeId !== exitNodeId || !oldSite.subnet)) { // This effectively moves the exit node to the new one exitNodeIdToQuery = exitNodeId; // Use the provided exitNodeId if it differs from the site's exitNodeId - const { exitNode, hasAccess } = await verifyExitNodeOrgAccess(exitNodeIdToQuery, oldSite.orgId); + const { exitNode, hasAccess } = await verifyExitNodeOrgAccess( + exitNodeIdToQuery, + oldSite.orgId + ); if (!exitNode) { logger.warn("Exit node not found"); @@ -114,6 +161,10 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { const blockSize = config.getRawConfig().gerbil.site_block_size; const subnets = sitesQuery .map((site) => site.subnet) + .filter( + (subnet) => + subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) + ) .filter((subnet) => subnet !== null); subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); const newSubnet = findNextAvailableCidr( @@ -122,7 +173,9 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { exitNode.address ); if (!newSubnet) { - logger.error("No available subnets found for the new exit node"); + logger.error( + `No available subnets found for the new exit node id ${exitNodeId} and site id ${siteId}` + ); return; } @@ -168,11 +221,25 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { return; } - // add the peer to the exit node - await addPeer(exitNodeIdToQuery, { - publicKey: publicKey, - allowedIps: [siteSubnet] - }); + try { + // add the peer to the exit node + await addPeer(exitNodeIdToQuery, { + publicKey: publicKey, + allowedIps: [siteSubnet] + }); + } catch (error) { + logger.error(`Failed to add peer to exit node: ${error}`); + } + + if (newtVersion && newtVersion !== newt.version) { + // update the newt version in the database + await db + .update(newts) + .set({ + version: newtVersion as string + }) + .where(eq(newts.newtId, newt.newtId)); + } if (newtVersion && newtVersion !== newt.version) { // update the newt version in the database @@ -194,10 +261,25 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { port: targets.port, internalPort: targets.internalPort, enabled: targets.enabled, - protocol: resources.protocol + protocol: resources.protocol, + hcEnabled: targetHealthCheck.hcEnabled, + hcPath: targetHealthCheck.hcPath, + hcScheme: targetHealthCheck.hcScheme, + hcMode: targetHealthCheck.hcMode, + hcHostname: targetHealthCheck.hcHostname, + hcPort: targetHealthCheck.hcPort, + hcInterval: targetHealthCheck.hcInterval, + hcUnhealthyInterval: targetHealthCheck.hcUnhealthyInterval, + hcTimeout: targetHealthCheck.hcTimeout, + hcHeaders: targetHealthCheck.hcHeaders, + hcMethod: targetHealthCheck.hcMethod }) .from(targets) .innerJoin(resources, eq(targets.resourceId, resources.resourceId)) + .leftJoin( + targetHealthCheck, + eq(targets.targetId, targetHealthCheck.targetId) + ) .where(and(eq(targets.siteId, siteId), eq(targets.enabled, true))); const { tcpTargets, udpTargets } = allTargets.reduce( @@ -222,6 +304,59 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { { tcpTargets: [] as string[], udpTargets: [] as string[] } ); + const healthCheckTargets = allTargets.map((target) => { + // make sure the stuff is defined + if ( + !target.hcPath || + !target.hcHostname || + !target.hcPort || + !target.hcInterval || + !target.hcMethod + ) { + logger.debug( + `Skipping target ${target.targetId} due to missing health check fields` + ); + return null; // Skip targets with missing health check fields + } + + // parse headers + const hcHeadersParse = target.hcHeaders + ? JSON.parse(target.hcHeaders) + : null; + let hcHeadersSend: { [key: string]: string } = {}; + if (hcHeadersParse) { + hcHeadersParse.forEach( + (header: { name: string; value: string }) => { + hcHeadersSend[header.name] = header.value; + } + ); + } + + return { + id: target.targetId, + hcEnabled: target.hcEnabled, + hcPath: target.hcPath, + hcScheme: target.hcScheme, + hcMode: target.hcMode, + hcHostname: target.hcHostname, + hcPort: target.hcPort, + hcInterval: target.hcInterval, // in seconds + hcUnhealthyInterval: target.hcUnhealthyInterval, // in seconds + hcTimeout: target.hcTimeout, // in seconds + hcHeaders: hcHeadersSend, + hcMethod: target.hcMethod + }; + }); + + // Filter out any null values from health check targets + const validHealthCheckTargets = healthCheckTargets.filter( + (target) => target !== null + ); + + logger.debug( + `Sending health check targets to newt ${newt.newtId}: ${JSON.stringify(validHealthCheckTargets)}` + ); + return { message: { type: "newt/wg/connect", @@ -233,10 +368,11 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { targets: { udp: udpTargets, tcp: tcpTargets - } + }, + healthCheckTargets: validHealthCheckTargets } }, broadcast: false, // Send to all clients excludeSender: false // Include sender in broadcast }; -}; \ No newline at end of file +}; diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index 803c3e27..c05a64a1 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -1,10 +1,12 @@ -import { Target } from "@server/db"; +import { Target, TargetHealthCheck, db, targetHealthCheck } from "@server/db"; import { sendToClient } from "../ws"; import logger from "@server/logger"; +import { eq, inArray } from "drizzle-orm"; export async function addTargets( newtId: string, targets: Target[], + healthCheckData: TargetHealthCheck[], protocol: string, port: number | null = null ) { @@ -21,6 +23,62 @@ export async function addTargets( targets: payloadTargets } }); + + // Create a map for quick lookup + const healthCheckMap = new Map(); + healthCheckData.forEach(hc => { + healthCheckMap.set(hc.targetId, hc); + }); + + const healthCheckTargets = targets.map((target) => { + const hc = healthCheckMap.get(target.targetId); + + // If no health check data found, skip this target + if (!hc) { + logger.warn(`No health check configuration found for target ${target.targetId}`); + return null; + } + + // Ensure all necessary fields are present + if (!hc.hcPath || !hc.hcHostname || !hc.hcPort || !hc.hcInterval || !hc.hcMethod) { + logger.debug(`Skipping target ${target.targetId} due to missing health check fields`); + return null; // Skip targets with missing health check fields + } + + const hcHeadersParse = hc.hcHeaders ? JSON.parse(hc.hcHeaders) : null; + const hcHeadersSend: { [key: string]: string } = {}; + if (hcHeadersParse) { + // transform + hcHeadersParse.forEach((header: { name: string; value: string }) => { + hcHeadersSend[header.name] = header.value; + }); + } + + return { + id: target.targetId, + hcEnabled: hc.hcEnabled, + hcPath: hc.hcPath, + hcScheme: hc.hcScheme, + hcMode: hc.hcMode, + hcHostname: hc.hcHostname, + hcPort: hc.hcPort, + hcInterval: hc.hcInterval, // in seconds + hcUnhealthyInterval: hc.hcUnhealthyInterval, // in seconds + hcTimeout: hc.hcTimeout, // in seconds + hcHeaders: hcHeadersSend, + hcMethod: hc.hcMethod + }; + }); + + // Filter out any null values from health check targets + const validHealthCheckTargets = healthCheckTargets.filter((target) => target !== null); + + await sendToClient(newtId, { + type: `newt/healthcheck/add`, + data: { + targets: validHealthCheckTargets + } + }); } export async function removeTargets( @@ -42,4 +100,15 @@ export async function removeTargets( targets: payloadTargets } }); + + const healthCheckTargets = targets.map((target) => { + return target.targetId + }); + + await sendToClient(newtId, { + type: `newt/healthcheck/remove`, + data: { + ids: healthCheckTargets + } + }); } diff --git a/server/routers/olm/handleOlmRegisterMessage.ts b/server/routers/olm/handleOlmRegisterMessage.ts index 11ca8b5e..33d7f9cb 100644 --- a/server/routers/olm/handleOlmRegisterMessage.ts +++ b/server/routers/olm/handleOlmRegisterMessage.ts @@ -88,10 +88,10 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { .where(eq(olms.olmId, olm.olmId)); } - if (now - (client.lastHolePunch || 0) > 6) { - logger.warn("Client last hole punch is too old, skipping all sites"); - return; - } + // if (now - (client.lastHolePunch || 0) > 6) { + // logger.warn("Client last hole punch is too old, skipping all sites"); + // return; + // } if (client.pubKey !== publicKey) { logger.info( @@ -145,7 +145,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => { // Validate endpoint and hole punch status if (!site.endpoint) { - logger.warn(`Site ${site.siteId} has no endpoint, skipping`); + logger.warn(`In olm register: site ${site.siteId} has no endpoint, skipping`); continue; } diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index d26774dd..a12520a4 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -24,6 +24,10 @@ import { fromError } from "zod-validation-error"; import { defaultRoleAllowedActions } from "../role"; import { OpenAPITags, registry } from "@server/openApi"; import { isValidCIDR } from "@server/lib/validators"; +import { createCustomer } from "@server/routers/private/billing/createCustomer"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { build } from "@server/build"; const createOrgSchema = z .object({ @@ -249,6 +253,19 @@ export async function createOrg( return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, error)); } + if (build == "saas") { + // make sure we have the stripe customer + const customerId = await createCustomer(orgId, req.user?.email); + if (customerId) { + await usageService.updateDaily( + orgId, + FeatureId.USERS, + 1, + customerId + ); // Only 1 because we are creating the org + } + } + return response(res, { data: org, success: true, diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index 35c1a5f7..e467374f 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -17,7 +17,7 @@ const getOrgSchema = z .strict(); export type GetOrgResponse = { - org: Org; + org: Org & { settings: { } | null }; }; registry.registerPath({ @@ -64,9 +64,27 @@ export async function getOrg( ); } + // Parse settings from JSON string back to object + let parsedSettings = null; + if (org[0].settings) { + try { + parsedSettings = JSON.parse(org[0].settings); + } catch (error) { + // If parsing fails, keep as string for backward compatibility + parsedSettings = org[0].settings; + } + } + + logger.info( + `returning data: ${JSON.stringify({ ...org[0], settings: parsedSettings })}` + ); + return response(res, { data: { - org: org[0] + org: { + ...org[0], + settings: parsedSettings + } }, success: true, error: false, diff --git a/server/routers/org/index.ts b/server/routers/org/index.ts index 754def66..013f6c6d 100644 --- a/server/routers/org/index.ts +++ b/server/routers/org/index.ts @@ -7,4 +7,5 @@ export * from "./checkId"; export * from "./getOrgOverview"; export * from "./listOrgs"; export * from "./pickOrgDefaults"; -export * from "./applyBlueprint"; \ No newline at end of file +export * from "./privateSendUsageNotifications"; +export * from "./applyBlueprint"; diff --git a/server/routers/org/privateSendUsageNotifications.ts b/server/routers/org/privateSendUsageNotifications.ts new file mode 100644 index 00000000..8b2a773d --- /dev/null +++ b/server/routers/org/privateSendUsageNotifications.ts @@ -0,0 +1,249 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { userOrgs, users, roles, orgs } from "@server/db"; +import { eq, and, or } from "drizzle-orm"; +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 { sendEmail } from "@server/emails"; +import NotifyUsageLimitApproaching from "@server/emails/templates/PrivateNotifyUsageLimitApproaching"; +import NotifyUsageLimitReached from "@server/emails/templates/PrivateNotifyUsageLimitReached"; +import config from "@server/lib/config"; +import { OpenAPITags, registry } from "@server/openApi"; + +const sendUsageNotificationParamsSchema = z.object({ + orgId: z.string() +}); + +const sendUsageNotificationBodySchema = z.object({ + notificationType: z.enum(["approaching_70", "approaching_90", "reached"]), + limitName: z.string(), + currentUsage: z.number(), + usageLimit: z.number(), +}); + +type SendUsageNotificationRequest = z.infer; + +export type SendUsageNotificationResponse = { + success: boolean; + emailsSent: number; + adminEmails: string[]; +}; + +// WE SHOULD NOT REGISTER THE PATH IN SAAS +// registry.registerPath({ +// method: "post", +// path: "/org/{orgId}/send-usage-notification", +// description: "Send usage limit notification emails to all organization admins.", +// tags: [OpenAPITags.Org], +// request: { +// params: sendUsageNotificationParamsSchema, +// body: { +// content: { +// "application/json": { +// schema: sendUsageNotificationBodySchema +// } +// } +// } +// }, +// responses: { +// 200: { +// description: "Usage notifications sent successfully", +// content: { +// "application/json": { +// schema: z.object({ +// success: z.boolean(), +// emailsSent: z.number(), +// adminEmails: z.array(z.string()) +// }) +// } +// } +// } +// } +// }); + +async function getOrgAdmins(orgId: string) { + // Get all users in the organization who are either: + // 1. Organization owners (isOwner = true) + // 2. Have admin roles (role.isAdmin = true) + const admins = await db + .select({ + userId: users.userId, + email: users.email, + name: users.name, + isOwner: userOrgs.isOwner, + roleName: roles.name, + isAdminRole: roles.isAdmin + }) + .from(userOrgs) + .innerJoin(users, eq(userOrgs.userId, users.userId)) + .leftJoin(roles, eq(userOrgs.roleId, roles.roleId)) + .where( + and( + eq(userOrgs.orgId, orgId), + or( + eq(userOrgs.isOwner, true), + eq(roles.isAdmin, true) + ) + ) + ); + + // Filter to only include users with verified emails + const orgAdmins = admins.filter(admin => + admin.email && + admin.email.length > 0 + ); + + return orgAdmins; +} + +export async function sendUsageNotification( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = sendUsageNotificationParamsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = sendUsageNotificationBodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + const { + notificationType, + limitName, + currentUsage, + usageLimit, + } = parsedBody.data; + + // Verify organization exists + const org = await db + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)) + .limit(1); + + if (org.length === 0) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Organization not found") + ); + } + + // Get all admin users for this organization + const orgAdmins = await getOrgAdmins(orgId); + + if (orgAdmins.length === 0) { + logger.warn(`No admin users found for organization ${orgId}`); + return response(res, { + data: { + success: true, + emailsSent: 0, + adminEmails: [] + }, + success: true, + error: false, + message: "No admin users found to notify", + status: HttpCode.OK + }); + } + + // Default billing link if not provided + const defaultBillingLink = `${config.getRawConfig().app.dashboard_url}/${orgId}/settings/billing`; + + let emailsSent = 0; + const adminEmails: string[] = []; + + // Send emails to all admin users + for (const admin of orgAdmins) { + if (!admin.email) continue; + + try { + let template; + let subject; + + if (notificationType === "approaching_70" || notificationType === "approaching_90") { + template = NotifyUsageLimitApproaching({ + email: admin.email, + limitName, + currentUsage, + usageLimit, + billingLink: defaultBillingLink + }); + subject = `Usage limit warning for ${limitName}`; + } else { + template = NotifyUsageLimitReached({ + email: admin.email, + limitName, + currentUsage, + usageLimit, + billingLink: defaultBillingLink + }); + subject = `URGENT: Usage limit reached for ${limitName}`; + } + + await sendEmail(template, { + to: admin.email, + from: config.getNoReplyEmail(), + subject + }); + + emailsSent++; + adminEmails.push(admin.email); + + logger.info(`Usage notification sent to admin ${admin.email} for org ${orgId}`); + } catch (emailError) { + logger.error(`Failed to send usage notification to ${admin.email}:`, emailError); + // Continue with other admins even if one fails + } + } + + return response(res, { + data: { + success: true, + emailsSent, + adminEmails + }, + success: true, + error: false, + message: `Usage notifications sent to ${emailsSent} administrators`, + status: HttpCode.OK + }); + + } catch (error) { + logger.error("Error sending usage notifications:", error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to send usage notifications") + ); + } +} diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 6dcd1016..6f30e62c 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -12,13 +12,15 @@ import { OpenAPITags, registry } from "@server/openApi"; const updateOrgParamsSchema = z .object({ - orgId: z.string() + orgId: z.string(), }) .strict(); const updateOrgBodySchema = z .object({ - name: z.string().min(1).max(255).optional() + name: z.string().min(1).max(255).optional(), + settings: z.object({ + }).optional(), }) .strict() .refine((data) => Object.keys(data).length > 0, { @@ -70,11 +72,15 @@ export async function updateOrg( } const { orgId } = parsedParams.data; - const updateData = parsedBody.data; + + const settings = parsedBody.data.settings ? JSON.stringify(parsedBody.data.settings) : undefined; const updatedOrg = await db .update(orgs) - .set(updateData) + .set({ + name: parsedBody.data.name, + settings: settings + }) .where(eq(orgs.orgId, orgId)) .returning(); diff --git a/server/routers/private/billing/createCheckoutSession.ts b/server/routers/private/billing/createCheckoutSession.ts new file mode 100644 index 00000000..67507b68 --- /dev/null +++ b/server/routers/private/billing/createCheckoutSession.ts @@ -0,0 +1,101 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import config from "@server/lib/config"; +import { fromError } from "zod-validation-error"; +import stripe from "@server/lib/private/stripe"; +import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/private/billing"; +import { getTierPriceSet, TierId } from "@server/lib/private/billing/tiers"; + +const createCheckoutSessionSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export async function createCheckoutSession( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = createCheckoutSessionSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + // check if we already have a customer for this org + const [customer] = await db + .select() + .from(customers) + .where(eq(customers.orgId, orgId)) + .limit(1); + + // If we don't have a customer, create one + if (!customer) { + // error + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "No customer found for this organization" + ) + ); + } + + const standardTierPrice = getTierPriceSet()[TierId.STANDARD]; + + const session = await stripe!.checkout.sessions.create({ + client_reference_id: orgId, // So we can look it up the org later on the webhook + billing_address_collection: "required", + line_items: [ + { + price: standardTierPrice, // Use the standard tier + quantity: 1 + }, + ...getLineItems(getStandardFeaturePriceSet()) + ], // Start with the standard feature set that matches the free limits + customer: customer.customerId, + mode: "subscription", + success_url: `${config.getRawConfig().app.dashboard_url}/${orgId}/settings/billing?success=true&session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${config.getRawConfig().app.dashboard_url}/${orgId}/settings/billing?canceled=true` + }); + + return response(res, { + data: session.url, + success: true, + error: false, + message: "Organization created successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/billing/createCustomer.ts b/server/routers/private/billing/createCustomer.ts new file mode 100644 index 00000000..d1c08a0e --- /dev/null +++ b/server/routers/private/billing/createCustomer.ts @@ -0,0 +1,48 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import stripe from "@server/lib/private/stripe"; +import { build } from "@server/build"; + +export async function createCustomer( + orgId: string, + email: string | null | undefined +): Promise { + if (build !== "saas") { + return; + } + + const [customer] = await db + .select() + .from(customers) + .where(eq(customers.orgId, orgId)) + .limit(1); + + let customerId: string; + // If we don't have a customer, create one + if (!customer) { + const newCustomer = await stripe!.customers.create({ + metadata: { + orgId: orgId + }, + email: email || undefined + }); + customerId = newCustomer.id; + // It will get inserted into the database by the webhook + } else { + customerId = customer.customerId; + } + return customerId; +} diff --git a/server/routers/private/billing/createPortalSession.ts b/server/routers/private/billing/createPortalSession.ts new file mode 100644 index 00000000..aa672377 --- /dev/null +++ b/server/routers/private/billing/createPortalSession.ts @@ -0,0 +1,89 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { account, customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import config from "@server/lib/config"; +import { fromError } from "zod-validation-error"; +import stripe from "@server/lib/private/stripe"; + +const createPortalSessionSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export async function createPortalSession( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = createPortalSessionSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + // check if we already have a customer for this org + const [customer] = await db + .select() + .from(customers) + .where(eq(customers.orgId, orgId)) + .limit(1); + + let customerId: string; + // If we don't have a customer, create one + if (!customer) { + // error + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "No customer found for this organization" + ) + ); + } else { + // If we have a customer, use the existing customer ID + customerId = customer.customerId; + } + const portalSession = await stripe!.billingPortal.sessions.create({ + customer: customerId, + return_url: `${config.getRawConfig().app.dashboard_url}/${orgId}/settings/billing` + }); + + return response(res, { + data: portalSession.url, + success: true, + error: false, + message: "Organization created successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/billing/getOrgSubscription.ts b/server/routers/private/billing/getOrgSubscription.ts new file mode 100644 index 00000000..3e4f575e --- /dev/null +++ b/server/routers/private/billing/getOrgSubscription.ts @@ -0,0 +1,157 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { Org, orgs } from "@server/db"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; +// Import tables for billing +import { + customers, + subscriptions, + subscriptionItems, + Subscription, + SubscriptionItem +} from "@server/db"; + +const getOrgSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export type GetOrgSubscriptionResponse = { + subscription: Subscription | null; + items: SubscriptionItem[]; +}; + +registry.registerPath({ + method: "get", + path: "/org/{orgId}/billing/subscription", + description: "Get an organization", + tags: [OpenAPITags.Org], + request: { + params: getOrgSchema + }, + responses: {} +}); + +export async function getOrgSubscription( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = getOrgSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromZodError(parsedParams.error) + ) + ); + } + + const { orgId } = parsedParams.data; + + let subscriptionData = null; + let itemsData: SubscriptionItem[] = []; + try { + const { subscription, items } = await getOrgSubscriptionData(orgId); + subscriptionData = subscription; + itemsData = items; + } catch (err) { + if ((err as Error).message === "Not found") { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Organization with ID ${orgId} not found` + ) + ); + } + throw err; + } + + return response(res, { + data: { + subscription: subscriptionData, + items: itemsData + }, + success: true, + error: false, + message: "Organization and subscription retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} + +export async function getOrgSubscriptionData( + orgId: string +): Promise<{ subscription: Subscription | null; items: SubscriptionItem[] }> { + const org = await db + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)) + .limit(1); + + if (org.length === 0) { + throw new Error(`Not found`); + } + + // Get customer for org + const customer = await db + .select() + .from(customers) + .where(eq(customers.orgId, orgId)) + .limit(1); + + let subscription = null; + let items: SubscriptionItem[] = []; + + if (customer.length > 0) { + // Get subscription for customer + const subs = await db + .select() + .from(subscriptions) + .where(eq(subscriptions.customerId, customer[0].customerId)) + .limit(1); + + if (subs.length > 0) { + subscription = subs[0]; + // Get subscription items + items = await db + .select() + .from(subscriptionItems) + .where( + eq( + subscriptionItems.subscriptionId, + subscription.subscriptionId + ) + ); + } + } + + return { subscription, items }; +} diff --git a/server/routers/private/billing/getOrgUsage.ts b/server/routers/private/billing/getOrgUsage.ts new file mode 100644 index 00000000..b387fd52 --- /dev/null +++ b/server/routers/private/billing/getOrgUsage.ts @@ -0,0 +1,129 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { orgs } from "@server/db"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; +import { Limit, limits, Usage, usage } from "@server/db"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; + +const getOrgSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export type GetOrgUsageResponse = { + usage: Usage[]; + limits: Limit[]; +}; + +registry.registerPath({ + method: "get", + path: "/org/{orgId}/billing/usage", + description: "Get an organization's billing usage", + tags: [OpenAPITags.Org], + request: { + params: getOrgSchema + }, + responses: {} +}); + +export async function getOrgUsage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = getOrgSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromZodError(parsedParams.error) + ) + ); + } + + const { orgId } = parsedParams.data; + + const org = await db + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)) + .limit(1); + + if (org.length === 0) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Organization with ID ${orgId} not found` + ) + ); + } + + // Get usage for org + let usageData = []; + + const siteUptime = await usageService.getUsage(orgId, FeatureId.SITE_UPTIME) + const users = await usageService.getUsageDaily(orgId, FeatureId.USERS) + const domains = await usageService.getUsageDaily(orgId, FeatureId.DOMAINS) + const remoteExitNodes = await usageService.getUsageDaily(orgId, FeatureId.REMOTE_EXIT_NODES) + const egressData = await usageService.getUsage(orgId, FeatureId.EGRESS_DATA_MB) + + if (siteUptime) { + usageData.push(siteUptime); + } + if (users) { + usageData.push(users); + } + if (egressData) { + usageData.push(egressData); + } + if (domains) { + usageData.push(domains); + } + if (remoteExitNodes) { + usageData.push(remoteExitNodes); + } + + const orgLimits = await db.select() + .from(limits) + .where(eq(limits.orgId, orgId)); + + return response(res, { + data: { + usage: usageData, + limits: orgLimits + }, + success: true, + error: false, + message: "Organization usage retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/billing/hooks/handleCustomerCreated.ts b/server/routers/private/billing/hooks/handleCustomerCreated.ts new file mode 100644 index 00000000..fdccc8dd --- /dev/null +++ b/server/routers/private/billing/hooks/handleCustomerCreated.ts @@ -0,0 +1,57 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import logger from "@server/logger"; + +export async function handleCustomerCreated( + customer: Stripe.Customer +): Promise { + try { + const [existingCustomer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, customer.id)) + .limit(1); + + if (existingCustomer) { + logger.info(`Customer with ID ${customer.id} already exists.`); + return; + } + + if (!customer.metadata.orgId) { + logger.error( + `Customer with ID ${customer.id} does not have an orgId in metadata.` + ); + return; + } + + await db.insert(customers).values({ + customerId: customer.id, + orgId: customer.metadata.orgId, + email: customer.email || null, + name: customer.name || null, + createdAt: customer.created, + updatedAt: customer.created + }); + logger.info(`Customer with ID ${customer.id} created successfully.`); + } catch (error) { + logger.error( + `Error handling customer created event for ID ${customer.id}:`, + error + ); + } + return; +} diff --git a/server/routers/private/billing/hooks/handleCustomerDeleted.ts b/server/routers/private/billing/hooks/handleCustomerDeleted.ts new file mode 100644 index 00000000..aa2e6964 --- /dev/null +++ b/server/routers/private/billing/hooks/handleCustomerDeleted.ts @@ -0,0 +1,44 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import logger from "@server/logger"; + +export async function handleCustomerDeleted( + customer: Stripe.Customer +): Promise { + try { + const [existingCustomer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, customer.id)) + .limit(1); + + if (!existingCustomer) { + logger.info(`Customer with ID ${customer.id} does not exist.`); + return; + } + + await db + .delete(customers) + .where(eq(customers.customerId, customer.id)); + } catch (error) { + logger.error( + `Error handling customer created event for ID ${customer.id}:`, + error + ); + } + return; +} diff --git a/server/routers/private/billing/hooks/handleCustomerUpdated.ts b/server/routers/private/billing/hooks/handleCustomerUpdated.ts new file mode 100644 index 00000000..3a0210a9 --- /dev/null +++ b/server/routers/private/billing/hooks/handleCustomerUpdated.ts @@ -0,0 +1,54 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { customers, db } from "@server/db"; +import { eq } from "drizzle-orm"; +import logger from "@server/logger"; + +export async function handleCustomerUpdated( + customer: Stripe.Customer +): Promise { + try { + const [existingCustomer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, customer.id)) + .limit(1); + + if (!existingCustomer) { + logger.info(`Customer with ID ${customer.id} does not exist.`); + return; + } + + const newCustomer = { + customerId: customer.id, + orgId: customer.metadata.orgId, + email: customer.email || null, + name: customer.name || null, + updatedAt: Math.floor(Date.now() / 1000) + }; + + // Update the existing customer record + await db + .update(customers) + .set(newCustomer) + .where(eq(customers.customerId, customer.id)); + } catch (error) { + logger.error( + `Error handling customer created event for ID ${customer.id}:`, + error + ); + } + return; +} diff --git a/server/routers/private/billing/hooks/handleSubscriptionCreated.ts b/server/routers/private/billing/hooks/handleSubscriptionCreated.ts new file mode 100644 index 00000000..ee6376c9 --- /dev/null +++ b/server/routers/private/billing/hooks/handleSubscriptionCreated.ts @@ -0,0 +1,153 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { + customers, + subscriptions, + db, + subscriptionItems, + userOrgs, + users +} from "@server/db"; +import { eq, and } from "drizzle-orm"; +import logger from "@server/logger"; +import stripe from "@server/lib/private/stripe"; +import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; +import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; + +export async function handleSubscriptionCreated( + subscription: Stripe.Subscription +): Promise { + try { + // Fetch the subscription from Stripe with expanded price.tiers + const fullSubscription = await stripe!.subscriptions.retrieve( + subscription.id, + { + expand: ["items.data.price.tiers"] + } + ); + + logger.info(JSON.stringify(fullSubscription, null, 2)); + // Check if subscription already exists + const [existingSubscription] = await db + .select() + .from(subscriptions) + .where(eq(subscriptions.subscriptionId, subscription.id)) + .limit(1); + + if (existingSubscription) { + logger.info( + `Subscription with ID ${subscription.id} already exists.` + ); + return; + } + + const newSubscription = { + subscriptionId: subscription.id, + customerId: subscription.customer as string, + status: subscription.status, + canceledAt: subscription.canceled_at + ? subscription.canceled_at + : null, + createdAt: subscription.created + }; + + await db.insert(subscriptions).values(newSubscription); + logger.info( + `Subscription with ID ${subscription.id} created successfully.` + ); + + // Insert subscription items + if (Array.isArray(fullSubscription.items?.data)) { + const itemsToInsertPromises = fullSubscription.items.data.map( + async (item) => { + // try to get the product name from stripe and add it to the item + let name = null; + if (item.price.product) { + const product = await stripe!.products.retrieve( + item.price.product as string + ); + name = product.name || null; + } + + return { + subscriptionId: subscription.id, + planId: item.plan.id, + priceId: item.price.id, + meterId: item.plan.meter, + unitAmount: item.price.unit_amount || 0, + currentPeriodStart: item.current_period_start, + currentPeriodEnd: item.current_period_end, + tiers: item.price.tiers + ? JSON.stringify(item.price.tiers) + : null, + interval: item.plan.interval, + name + }; + } + ); + + // wait for all items to be processed + const itemsToInsert = await Promise.all(itemsToInsertPromises); + + if (itemsToInsert.length > 0) { + await db.insert(subscriptionItems).values(itemsToInsert); + logger.info( + `Inserted ${itemsToInsert.length} subscription items for subscription ${subscription.id}.` + ); + } + } + + // Lookup customer to get orgId + const [customer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, subscription.customer as string)) + .limit(1); + + if (!customer) { + logger.error( + `Customer with ID ${subscription.customer} not found for subscription ${subscription.id}.` + ); + return; + } + + await handleSubscriptionLifesycle(customer.orgId, subscription.status); + + const [orgUserRes] = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.orgId, customer.orgId), + eq(userOrgs.isOwner, true) + ) + ) + .innerJoin(users, eq(userOrgs.userId, users.userId)); + + if (orgUserRes) { + const email = orgUserRes.user.email; + + if (email) { + moveEmailToAudience(email, AudienceIds.Subscribed); + } + } + } catch (error) { + logger.error( + `Error handling subscription created event for ID ${subscription.id}:`, + error + ); + } + return; +} diff --git a/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts b/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts new file mode 100644 index 00000000..95123731 --- /dev/null +++ b/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts @@ -0,0 +1,91 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { subscriptions, db, subscriptionItems, customers, userOrgs, users } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import logger from "@server/logger"; +import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; +import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; + +export async function handleSubscriptionDeleted( + subscription: Stripe.Subscription +): Promise { + try { + const [existingSubscription] = await db + .select() + .from(subscriptions) + .where(eq(subscriptions.subscriptionId, subscription.id)) + .limit(1); + + if (!existingSubscription) { + logger.info( + `Subscription with ID ${subscription.id} does not exist.` + ); + return; + } + + await db + .delete(subscriptions) + .where(eq(subscriptions.subscriptionId, subscription.id)); + + await db + .delete(subscriptionItems) + .where(eq(subscriptionItems.subscriptionId, subscription.id)); + + + // Lookup customer to get orgId + const [customer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, subscription.customer as string)) + .limit(1); + + if (!customer) { + logger.error( + `Customer with ID ${subscription.customer} not found for subscription ${subscription.id}.` + ); + return; + } + + await handleSubscriptionLifesycle( + customer.orgId, + subscription.status + ); + + const [orgUserRes] = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.orgId, customer.orgId), + eq(userOrgs.isOwner, true) + ) + ) + .innerJoin(users, eq(userOrgs.userId, users.userId)); + + if (orgUserRes) { + const email = orgUserRes.user.email; + + if (email) { + moveEmailToAudience(email, AudienceIds.Churned); + } + } + } catch (error) { + logger.error( + `Error handling subscription updated event for ID ${subscription.id}:`, + error + ); + } + return; +} diff --git a/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts b/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts new file mode 100644 index 00000000..f1cbcafe --- /dev/null +++ b/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts @@ -0,0 +1,296 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import Stripe from "stripe"; +import { + subscriptions, + db, + subscriptionItems, + usage, + sites, + customers, + orgs +} from "@server/db"; +import { eq, and } from "drizzle-orm"; +import logger from "@server/logger"; +import { getFeatureIdByMetricId } from "@server/lib/private/billing/features"; +import stripe from "@server/lib/private/stripe"; +import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; + +export async function handleSubscriptionUpdated( + subscription: Stripe.Subscription, + previousAttributes: Partial | undefined +): Promise { + try { + // Fetch the subscription from Stripe with expanded price.tiers + const fullSubscription = await stripe!.subscriptions.retrieve( + subscription.id, + { + expand: ["items.data.price.tiers"] + } + ); + + logger.info(JSON.stringify(fullSubscription, null, 2)); + + const [existingSubscription] = await db + .select() + .from(subscriptions) + .where(eq(subscriptions.subscriptionId, subscription.id)) + .limit(1); + + if (!existingSubscription) { + logger.info( + `Subscription with ID ${subscription.id} does not exist.` + ); + return; + } + + // get the customer + const [existingCustomer] = await db + .select() + .from(customers) + .where(eq(customers.customerId, subscription.customer as string)) + .limit(1); + + await db + .update(subscriptions) + .set({ + status: subscription.status, + canceledAt: subscription.canceled_at + ? subscription.canceled_at + : null, + updatedAt: Math.floor(Date.now() / 1000), + billingCycleAnchor: subscription.billing_cycle_anchor + }) + .where(eq(subscriptions.subscriptionId, subscription.id)); + + await handleSubscriptionLifesycle( + existingCustomer.orgId, + subscription.status + ); + + // Upsert subscription items + if (Array.isArray(fullSubscription.items?.data)) { + const itemsToUpsert = fullSubscription.items.data.map((item) => ({ + subscriptionId: subscription.id, + planId: item.plan.id, + priceId: item.price.id, + meterId: item.plan.meter, + unitAmount: item.price.unit_amount || 0, + currentPeriodStart: item.current_period_start, + currentPeriodEnd: item.current_period_end, + tiers: item.price.tiers + ? JSON.stringify(item.price.tiers) + : null, + interval: item.plan.interval + })); + if (itemsToUpsert.length > 0) { + await db.transaction(async (trx) => { + await trx + .delete(subscriptionItems) + .where( + eq( + subscriptionItems.subscriptionId, + subscription.id + ) + ); + + await trx.insert(subscriptionItems).values(itemsToUpsert); + }); + logger.info( + `Updated ${itemsToUpsert.length} subscription items for subscription ${subscription.id}.` + ); + } + + // --- Detect cycled items and update usage --- + if (previousAttributes) { + // Only proceed if latest_invoice changed (per Stripe docs) + if ("latest_invoice" in previousAttributes) { + // If items array present in previous_attributes, check each item + if (Array.isArray(previousAttributes.items?.data)) { + for (const item of subscription.items.data) { + const prevItem = previousAttributes.items.data.find( + (pi: any) => pi.id === item.id + ); + if ( + prevItem && + prevItem.current_period_end && + item.current_period_start && + prevItem.current_period_end === + item.current_period_start && + item.current_period_start > + prevItem.current_period_start + ) { + logger.info( + `Subscription item ${item.id} has cycled. Resetting usage.` + ); + } else { + continue; + } + + // This item has cycled + const meterId = item.plan.meter; + if (!meterId) { + logger.warn( + `No meterId found for subscription item ${item.id}. Skipping usage reset.` + ); + continue; + } + const featureId = getFeatureIdByMetricId(meterId); + if (!featureId) { + logger.warn( + `No featureId found for meterId ${meterId}. Skipping usage reset.` + ); + continue; + } + + const orgId = existingCustomer.orgId; + + if (!orgId) { + logger.warn( + `No orgId found in subscription metadata for subscription ${subscription.id}. Skipping usage reset.` + ); + continue; + } + + await db.transaction(async (trx) => { + const [usageRow] = await trx + .select() + .from(usage) + .where( + eq( + usage.usageId, + `${orgId}-${featureId}` + ) + ) + .limit(1); + + if (usageRow) { + // get the next rollover date + + const [org] = await trx + .select() + .from(orgs) + .where(eq(orgs.orgId, orgId)) + .limit(1); + + const lastRollover = usageRow.rolledOverAt + ? new Date(usageRow.rolledOverAt * 1000) + : new Date(); + const anchorDate = org.createdAt + ? new Date(org.createdAt) + : new Date(); + + const nextRollover = + calculateNextRollOverDate( + lastRollover, + anchorDate + ); + + await trx + .update(usage) + .set({ + previousValue: usageRow.latestValue, + latestValue: + usageRow.instantaneousValue || + 0, + updatedAt: Math.floor( + Date.now() / 1000 + ), + rolledOverAt: Math.floor( + Date.now() / 1000 + ), + nextRolloverAt: Math.floor( + nextRollover.getTime() / 1000 + ) + }) + .where( + eq(usage.usageId, usageRow.usageId) + ); + logger.info( + `Usage reset for org ${orgId}, meter ${featureId} on subscription item cycle.` + ); + } + + // Also reset the sites to 0 + await trx + .update(sites) + .set({ + megabytesIn: 0, + megabytesOut: 0 + }) + .where(eq(sites.orgId, orgId)); + }); + } + } + } + } + // --- end usage update --- + } + } catch (error) { + logger.error( + `Error handling subscription updated event for ID ${subscription.id}:`, + error + ); + } + return; +} + +/** + * Calculate the next billing date based on monthly billing cycle + * Handles end-of-month scenarios as described in the requirements + * Made public for testing + */ +function calculateNextRollOverDate(lastRollover: Date, anchorDate: Date): Date { + const rolloverDate = new Date(lastRollover); + const anchor = new Date(anchorDate); + + // Get components from rollover date + const rolloverYear = rolloverDate.getUTCFullYear(); + const rolloverMonth = rolloverDate.getUTCMonth(); + + // Calculate target month and year (next month) + let targetMonth = rolloverMonth + 1; + let targetYear = rolloverYear; + + if (targetMonth > 11) { + targetMonth = 0; + targetYear++; + } + + // Get anchor day for billing + const anchorDay = anchor.getUTCDate(); + + // Get the last day of the target month + const lastDayOfMonth = new Date( + Date.UTC(targetYear, targetMonth + 1, 0) + ).getUTCDate(); + + // Use the anchor day or the last day of the month, whichever is smaller + const targetDay = Math.min(anchorDay, lastDayOfMonth); + + // Create the next billing date using UTC + const nextBilling = new Date( + Date.UTC( + targetYear, + targetMonth, + targetDay, + anchor.getUTCHours(), + anchor.getUTCMinutes(), + anchor.getUTCSeconds(), + anchor.getUTCMilliseconds() + ) + ); + + return nextBilling; +} diff --git a/server/routers/private/billing/index.ts b/server/routers/private/billing/index.ts new file mode 100644 index 00000000..913ae865 --- /dev/null +++ b/server/routers/private/billing/index.ts @@ -0,0 +1,18 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./createCheckoutSession"; +export * from "./createPortalSession"; +export * from "./getOrgSubscription"; +export * from "./getOrgUsage"; +export * from "./internalGetOrgTier"; \ No newline at end of file diff --git a/server/routers/private/billing/internalGetOrgTier.ts b/server/routers/private/billing/internalGetOrgTier.ts new file mode 100644 index 00000000..7f8cc642 --- /dev/null +++ b/server/routers/private/billing/internalGetOrgTier.ts @@ -0,0 +1,119 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; +import { getTierPriceSet } from "@server/lib/private/billing/tiers"; +import { getOrgSubscriptionData } from "./getOrgSubscription"; +import { build } from "@server/build"; + +const getOrgSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export type GetOrgTierResponse = { + tier: string | null; + active: boolean; +}; + +export async function getOrgTier( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = getOrgSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromZodError(parsedParams.error) + ) + ); + } + + const { orgId } = parsedParams.data; + + let tierData = null; + let activeData = false; + + try { + const { tier, active } = await getOrgTierData(orgId); + tierData = tier; + activeData = active; + } catch (err) { + if ((err as Error).message === "Not found") { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Organization with ID ${orgId} not found` + ) + ); + } + throw err; + } + + return response(res, { + data: { + tier: tierData, + active: activeData + }, + success: true, + error: false, + message: "Organization and subscription retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} + +export async function getOrgTierData( + orgId: string +): Promise<{ tier: string | null; active: boolean }> { + let tier = null; + let active = false; + + if (build !== "saas") { + return { tier, active }; + } + + const { subscription, items } = await getOrgSubscriptionData(orgId); + + if (items && items.length > 0) { + const tierPriceSet = getTierPriceSet(); + // Iterate through tiers in order (earlier keys are higher tiers) + for (const [tierId, priceId] of Object.entries(tierPriceSet)) { + // Check if any subscription item matches this tier's price ID + const matchingItem = items.find((item) => item.priceId === priceId); + if (matchingItem) { + tier = tierId; + break; + } + } + } + if (subscription && subscription.status === "active") { + active = true; + } + return { tier, active }; +} diff --git a/server/routers/private/billing/subscriptionLifecycle.ts b/server/routers/private/billing/subscriptionLifecycle.ts new file mode 100644 index 00000000..82dbfdbe --- /dev/null +++ b/server/routers/private/billing/subscriptionLifecycle.ts @@ -0,0 +1,45 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/private/billing/usageService"; +import logger from "@server/logger"; + +export async function handleSubscriptionLifesycle(orgId: string, status: string) { + switch (status) { + case "active": + await limitsService.applyLimitSetToOrg(orgId, subscribedLimitSet); + await usageService.checkLimitSet(orgId, true); + break; + case "canceled": + await limitsService.applyLimitSetToOrg(orgId, freeLimitSet); + await usageService.checkLimitSet(orgId, true); + break; + case "past_due": + // Optionally handle past due status, e.g., notify customer + break; + case "unpaid": + await limitsService.applyLimitSetToOrg(orgId, freeLimitSet); + await usageService.checkLimitSet(orgId, true); + break; + case "incomplete": + // Optionally handle incomplete status, e.g., notify customer + break; + case "incomplete_expired": + await limitsService.applyLimitSetToOrg(orgId, freeLimitSet); + await usageService.checkLimitSet(orgId, true); + break; + default: + break; + } +} \ No newline at end of file diff --git a/server/routers/private/billing/webhooks.ts b/server/routers/private/billing/webhooks.ts new file mode 100644 index 00000000..2844943a --- /dev/null +++ b/server/routers/private/billing/webhooks.ts @@ -0,0 +1,136 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import stripe from "@server/lib/private/stripe"; +import config from "@server/lib/config"; +import logger from "@server/logger"; +import createHttpError from "http-errors"; +import { response } from "@server/lib/response"; +import { Request, Response, NextFunction } from "express"; +import HttpCode from "@server/types/HttpCode"; +import Stripe from "stripe"; +import { handleCustomerCreated } from "./hooks/handleCustomerCreated"; +import { handleSubscriptionCreated } from "./hooks/handleSubscriptionCreated"; +import { handleSubscriptionUpdated } from "./hooks/handleSubscriptionUpdated"; +import { handleCustomerUpdated } from "./hooks/handleCustomerUpdated"; +import { handleSubscriptionDeleted } from "./hooks/handleSubscriptionDeleted"; +import { handleCustomerDeleted } from "./hooks/handleCustomerDeleted"; + +export async function stripeWebhookHandler( + req: Request, + res: Response, + next: NextFunction +): Promise { + let event: Stripe.Event = req.body; + const endpointSecret = config.getRawPrivateConfig().stripe?.webhook_secret; + if (!endpointSecret) { + logger.warn("Stripe webhook secret is not configured. Webhook events will not be priocessed."); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "") + ); + } + + // Only verify the event if you have an endpoint secret defined. + // Otherwise use the basic event deserialized with JSON.parse + if (endpointSecret) { + // Get the signature sent by Stripe + const signature = req.headers["stripe-signature"]; + + if (!signature) { + logger.info("No stripe signature found in headers."); + return next( + createHttpError(HttpCode.BAD_REQUEST, "No stripe signature found in headers") + ); + } + + try { + event = stripe!.webhooks.constructEvent( + req.body, + signature, + endpointSecret + ); + } catch (err) { + logger.error(`Webhook signature verification failed.`, err); + return next( + createHttpError(HttpCode.UNAUTHORIZED, "Webhook signature verification failed") + ); + } + } + let subscription; + let previousAttributes; + // Handle the event + switch (event.type) { + case "customer.created": + const customer = event.data.object; + logger.info("Customer created: ", customer); + handleCustomerCreated(customer); + break; + case "customer.updated": + const customerUpdated = event.data.object; + logger.info("Customer updated: ", customerUpdated); + handleCustomerUpdated(customerUpdated); + break; + case "customer.deleted": + const customerDeleted = event.data.object; + logger.info("Customer deleted: ", customerDeleted); + handleCustomerDeleted(customerDeleted); + break; + case "customer.subscription.paused": + subscription = event.data.object; + previousAttributes = event.data.previous_attributes; + handleSubscriptionUpdated(subscription, previousAttributes); + break; + case "customer.subscription.resumed": + subscription = event.data.object; + previousAttributes = event.data.previous_attributes; + handleSubscriptionUpdated(subscription, previousAttributes); + break; + case "customer.subscription.deleted": + subscription = event.data.object; + handleSubscriptionDeleted(subscription); + break; + case "customer.subscription.created": + subscription = event.data.object; + handleSubscriptionCreated(subscription); + break; + case "customer.subscription.updated": + subscription = event.data.object; + previousAttributes = event.data.previous_attributes; + handleSubscriptionUpdated(subscription, previousAttributes); + break; + case "customer.subscription.trial_will_end": + subscription = event.data.object; + // Then define and call a method to handle the subscription trial ending. + // handleSubscriptionTrialEnding(subscription); + break; + case "entitlements.active_entitlement_summary.updated": + subscription = event.data.object; + logger.info( + `Active entitlement summary updated for ${subscription}.` + ); + // Then define and call a method to handle active entitlement summary updated + // handleEntitlementUpdated(subscription); + break; + default: + // Unexpected event type + logger.info(`Unhandled event type ${event.type}.`); + } + // Return a 200 response to acknowledge receipt of the event + return response(res, { + data: null, + success: true, + error: false, + message: "Webhook event processed successfully", + status: HttpCode.CREATED + }); +} diff --git a/server/routers/private/certificates/createCertificate.ts b/server/routers/private/certificates/createCertificate.ts new file mode 100644 index 00000000..210878ef --- /dev/null +++ b/server/routers/private/certificates/createCertificate.ts @@ -0,0 +1,85 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Certificate, certificates, db, domains } from "@server/db"; +import logger from "@server/logger"; +import { Transaction } from "@server/db"; +import { eq, or, and, like } from "drizzle-orm"; +import { build } from "@server/build"; + +/** + * Checks if a certificate exists for the given domain. + * If not, creates a new certificate in 'pending' state. + * Wildcard certs cover subdomains. + */ +export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { + if (build !== "saas") { + return; + } + + const [domainRecord] = await trx + .select() + .from(domains) + .where(eq(domains.domainId, domainId)) + .limit(1); + + if (!domainRecord) { + throw new Error(`Domain with ID ${domainId} not found`); + } + + let existing: Certificate[] = []; + if (domainRecord.type == "ns") { + const domainLevelDown = domain.split('.').slice(1).join('.'); + existing = await trx + .select() + .from(certificates) + .where( + and( + eq(certificates.domainId, domainId), + eq(certificates.wildcard, true), // only NS domains can have wildcard certs + or( + eq(certificates.domain, domain), + eq(certificates.domain, domainLevelDown), + ) + ) + ); + } else { + // For non-NS domains, we only match exact domain names + existing = await trx + .select() + .from(certificates) + .where( + and( + eq(certificates.domainId, domainId), + eq(certificates.domain, domain) // exact match for non-NS domains + ) + ); + } + + if (existing.length > 0) { + logger.info( + `Certificate already exists for domain ${domain}` + ); + return; + } + + // No cert found, create a new one in pending state + await trx.insert(certificates).values({ + domain, + domainId, + wildcard: domainRecord.type == "ns", // we can only create wildcard certs for NS domains + status: "pending", + updatedAt: Math.floor(Date.now() / 1000), + createdAt: Math.floor(Date.now() / 1000) + }); +} diff --git a/server/routers/private/certificates/getCertificate.ts b/server/routers/private/certificates/getCertificate.ts new file mode 100644 index 00000000..a0bf74f6 --- /dev/null +++ b/server/routers/private/certificates/getCertificate.ts @@ -0,0 +1,167 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { certificates, db, domains } from "@server/db"; +import { eq, and, or, like } from "drizzle-orm"; +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 { registry } from "@server/openApi"; + +const getCertificateSchema = z + .object({ + domainId: z.string(), + domain: z.string().min(1).max(255), + orgId: z.string() + }) + .strict(); + +async function query(domainId: string, domain: string) { + const [domainRecord] = await db + .select() + .from(domains) + .where(eq(domains.domainId, domainId)) + .limit(1); + + if (!domainRecord) { + throw new Error(`Domain with ID ${domainId} not found`); + } + + let existing: any[] = []; + if (domainRecord.type == "ns") { + const domainLevelDown = domain.split('.').slice(1).join('.'); + + existing = await db + .select({ + certId: certificates.certId, + domain: certificates.domain, + wildcard: certificates.wildcard, + status: certificates.status, + expiresAt: certificates.expiresAt, + lastRenewalAttempt: certificates.lastRenewalAttempt, + createdAt: certificates.createdAt, + updatedAt: certificates.updatedAt, + errorMessage: certificates.errorMessage, + renewalCount: certificates.renewalCount + }) + .from(certificates) + .where( + and( + eq(certificates.domainId, domainId), + eq(certificates.wildcard, true), // only NS domains can have wildcard certs + or( + eq(certificates.domain, domain), + eq(certificates.domain, domainLevelDown), + ) + ) + ); + } else { + // For non-NS domains, we only match exact domain names + existing = await db + .select({ + certId: certificates.certId, + domain: certificates.domain, + wildcard: certificates.wildcard, + status: certificates.status, + expiresAt: certificates.expiresAt, + lastRenewalAttempt: certificates.lastRenewalAttempt, + createdAt: certificates.createdAt, + updatedAt: certificates.updatedAt, + errorMessage: certificates.errorMessage, + renewalCount: certificates.renewalCount + }) + .from(certificates) + .where( + and( + eq(certificates.domainId, domainId), + eq(certificates.domain, domain) // exact match for non-NS domains + ) + ); + } + + return existing.length > 0 ? existing[0] : null; +} + +export type GetCertificateResponse = { + certId: number; + domain: string; + domainId: string; + wildcard: boolean; + status: string; // pending, requested, valid, expired, failed + expiresAt: string | null; + lastRenewalAttempt: Date | null; + createdAt: string; + updatedAt: string; + errorMessage?: string | null; + renewalCount: number; +} + +registry.registerPath({ + method: "get", + path: "/org/{orgId}/certificate/{domainId}/{domain}", + description: "Get a certificate by domain.", + tags: ["Certificate"], + request: { + params: z.object({ + domainId: z + .string(), + domain: z.string().min(1).max(255), + orgId: z.string() + }) + }, + responses: {} +}); + +export async function getCertificate( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = getCertificateSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { domainId, domain } = parsedParams.data; + + const cert = await query(domainId, domain); + + if (!cert) { + logger.warn(`Certificate not found for domain: ${domainId}`); + return next(createHttpError(HttpCode.NOT_FOUND, "Certificate not found")); + } + + return response(res, { + data: cert, + success: true, + error: false, + message: "Certificate retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/certificates/index.ts b/server/routers/private/certificates/index.ts new file mode 100644 index 00000000..e1b81ae1 --- /dev/null +++ b/server/routers/private/certificates/index.ts @@ -0,0 +1,15 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./getCertificate"; +export * from "./restartCertificate"; \ No newline at end of file diff --git a/server/routers/private/certificates/restartCertificate.ts b/server/routers/private/certificates/restartCertificate.ts new file mode 100644 index 00000000..1ad3f6a7 --- /dev/null +++ b/server/routers/private/certificates/restartCertificate.ts @@ -0,0 +1,116 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { certificates, db } from "@server/db"; +import { sites } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import stoi from "@server/lib/stoi"; +import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; + +const restartCertificateParamsSchema = z + .object({ + certId: z.string().transform(stoi).pipe(z.number().int().positive()), + orgId: z.string() + }) + .strict(); + +registry.registerPath({ + method: "post", + path: "/certificate/{certId}", + description: "Restart a certificate by ID.", + tags: ["Certificate"], + request: { + params: z.object({ + certId: z + .string() + .transform(stoi) + .pipe(z.number().int().positive()), + orgId: z.string() + }) + }, + responses: {} +}); + +export async function restartCertificate( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = restartCertificateParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { certId } = parsedParams.data; + + // get the certificate by ID + const [cert] = await db + .select() + .from(certificates) + .where(eq(certificates.certId, certId)) + .limit(1); + + if (!cert) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Certificate not found") + ); + } + + if (cert.status != "failed" && cert.status != "expired") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Certificate is already valid, no need to restart" + ) + ); + } + + // update the certificate status to 'pending' + await db + .update(certificates) + .set({ + status: "pending", + errorMessage: null, + lastRenewalAttempt: Math.floor(Date.now() / 1000) + }) + .where(eq(certificates.certId, certId)); + + return response(res, { + data: null, + success: true, + error: false, + message: "Certificate restarted successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/hybrid.ts b/server/routers/private/hybrid.ts new file mode 100644 index 00000000..c8b0960d --- /dev/null +++ b/server/routers/private/hybrid.ts @@ -0,0 +1,1485 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { verifySessionRemoteExitNodeMiddleware } from "@server/middlewares/private/verifyRemoteExitNode"; +import { Router } from "express"; +import { + db, + exitNodes, + Resource, + ResourcePassword, + ResourcePincode, + Session, + User, + certificates, + exitNodeOrgs, + RemoteExitNode, + olms, + newts, + clients, + sites, + domains, + orgDomains, + targets, + loginPage, + loginPageOrg, + LoginPage +} from "@server/db"; +import { + resources, + resourcePincode, + resourcePassword, + sessions, + users, + userOrgs, + roleResources, + userResources, + resourceRules +} from "@server/db"; +import { eq, and, inArray, isNotNull, ne } from "drizzle-orm"; +import { response } from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import { NextFunction, Request, Response } from "express"; +import createHttpError from "http-errors"; +import { z } from "zod"; +import { fromError } from "zod-validation-error"; +import { getTraefikConfig } from "../../lib/traefik" +import { + generateGerbilConfig, + generateRelayMappings, + updateAndGenerateEndpointDestinations, + updateSiteBandwidth +} from "../gerbil"; +import * as gerbil from "@server/routers/gerbil"; +import logger from "@server/logger"; +import { decryptData } from "@server/lib/encryption"; +import { config } from "@server/lib/config"; +import * as fs from "fs"; +import { exchangeSession } from "../badger"; +import { validateResourceSessionToken } from "@server/auth/sessions/resource"; +import { checkExitNodeOrg, resolveExitNodes } from "@server/lib/exitNodes"; +import { maxmindLookup } from "@server/db/maxmind"; + +// Zod schemas for request validation +const getResourceByDomainParamsSchema = z + .object({ + domain: z.string().min(1, "Domain is required") + }) + .strict(); + +const getUserSessionParamsSchema = z + .object({ + userSessionId: z.string().min(1, "User session ID is required") + }) + .strict(); + +const getUserOrgRoleParamsSchema = z + .object({ + userId: z.string().min(1, "User ID is required"), + orgId: z.string().min(1, "Organization ID is required") + }) + .strict(); + +const getRoleResourceAccessParamsSchema = z + .object({ + roleId: z + .string() + .transform(Number) + .pipe( + z.number().int().positive("Role ID must be a positive integer") + ), + resourceId: z + .string() + .transform(Number) + .pipe( + z + .number() + .int() + .positive("Resource ID must be a positive integer") + ) + }) + .strict(); + +const getUserResourceAccessParamsSchema = z + .object({ + userId: z.string().min(1, "User ID is required"), + resourceId: z + .string() + .transform(Number) + .pipe( + z + .number() + .int() + .positive("Resource ID must be a positive integer") + ) + }) + .strict(); + +const getResourceRulesParamsSchema = z + .object({ + resourceId: z + .string() + .transform(Number) + .pipe( + z + .number() + .int() + .positive("Resource ID must be a positive integer") + ) + }) + .strict(); + +const validateResourceSessionTokenParamsSchema = z + .object({ + resourceId: z + .string() + .transform(Number) + .pipe( + z + .number() + .int() + .positive("Resource ID must be a positive integer") + ) + }) + .strict(); + +const validateResourceSessionTokenBodySchema = z + .object({ + token: z.string().min(1, "Token is required") + }) + .strict(); + +// Certificates by domains query validation +const getCertificatesByDomainsQuerySchema = z + .object({ + // Accept domains as string or array (domains or domains[]) + domains: z + .union([z.array(z.string().min(1)), z.string().min(1)]) + .optional(), + // Handle array format from query parameters (domains[]) + "domains[]": z + .union([z.array(z.string().min(1)), z.string().min(1)]) + .optional() + }) + .strict(); + +// Type exports for request schemas +export type GetResourceByDomainParams = z.infer< + typeof getResourceByDomainParamsSchema +>; +export type GetUserSessionParams = z.infer; +export type GetUserOrgRoleParams = z.infer; +export type GetRoleResourceAccessParams = z.infer< + typeof getRoleResourceAccessParamsSchema +>; +export type GetUserResourceAccessParams = z.infer< + typeof getUserResourceAccessParamsSchema +>; +export type GetResourceRulesParams = z.infer< + typeof getResourceRulesParamsSchema +>; +export type ValidateResourceSessionTokenParams = z.infer< + typeof validateResourceSessionTokenParamsSchema +>; +export type ValidateResourceSessionTokenBody = z.infer< + typeof validateResourceSessionTokenBodySchema +>; + +// Type definitions for API responses +export type ResourceWithAuth = { + resource: Resource | null; + pincode: ResourcePincode | null; + password: ResourcePassword | null; +}; + +export type UserSessionWithUser = { + session: Session | null; + user: User | null; +}; + +// Root routes +const hybridRouter = Router(); +hybridRouter.use(verifySessionRemoteExitNodeMiddleware); + +hybridRouter.get( + "/traefik-config", + async (req: Request, res: Response, next: NextFunction) => { + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + try { + const traefikConfig = await getTraefikConfig( + remoteExitNode.exitNodeId, + ["newt", "local", "wireguard"], // Allow them to use all the site types + true // But don't allow domain namespace resources + ); + return response(res, { + data: traefikConfig, + success: true, + error: false, + message: "Traefik config retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get Traefik config" + ) + ); + } + } +); + +// Get valid certificates for given domains (supports wildcard certs) +hybridRouter.get( + "/certificates/domains", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsed = getCertificatesByDomainsQuerySchema.safeParse( + req.query + ); + if (!parsed.success) { + logger.info("Invalid query parameters:", parsed.error); + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsed.error).toString() + ) + ); + } + + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + logger.error("Remote exit node not found"); + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + // Normalize domains into a unique array + const rawDomains = parsed.data.domains; + const rawDomainsArray = parsed.data["domains[]"]; + + // Combine both possible sources + const allRawDomains = [ + ...(Array.isArray(rawDomains) + ? rawDomains + : rawDomains + ? [rawDomains] + : []), + ...(Array.isArray(rawDomainsArray) + ? rawDomainsArray + : rawDomainsArray + ? [rawDomainsArray] + : []) + ]; + + const uniqueDomains = Array.from( + new Set( + allRawDomains + .map((d) => (typeof d === "string" ? d.trim() : "")) + .filter((d) => d.length > 0) + ) + ); + + if (uniqueDomains.length === 0) { + return response(res, { + data: [], + success: true, + error: false, + message: "No domains provided", + status: HttpCode.OK + }); + } + + // Build candidate domain list: exact + first-suffix for wildcard lookup + const suffixes = uniqueDomains + .map((domain) => { + const firstDot = domain.indexOf("."); + return firstDot > 0 ? domain.slice(firstDot + 1) : null; + }) + .filter((d): d is string => !!d); + + const candidateDomains = Array.from( + new Set([...uniqueDomains, ...suffixes]) + ); + + // Query certificates with domain and org information to check authorization + const certRows = await db + .select({ + id: certificates.certId, + domain: certificates.domain, + certFile: certificates.certFile, + keyFile: certificates.keyFile, + expiresAt: certificates.expiresAt, + updatedAt: certificates.updatedAt, + wildcard: certificates.wildcard, + domainId: certificates.domainId, + orgId: orgDomains.orgId + }) + .from(certificates) + .leftJoin(domains, eq(domains.domainId, certificates.domainId)) + .leftJoin(orgDomains, eq(orgDomains.domainId, domains.domainId)) + .where( + and( + eq(certificates.status, "valid"), + isNotNull(certificates.certFile), + isNotNull(certificates.keyFile), + inArray(certificates.domain, candidateDomains) + ) + ); + + // Filter certificates based on wildcard matching and exit node authorization + const filtered = []; + for (const cert of certRows) { + // Check if the domain matches our request + const domainMatches = + uniqueDomains.includes(cert.domain) || + (cert.wildcard === true && + uniqueDomains.some((d) => + d.endsWith(`.${cert.domain}`) + )); + + if (!domainMatches) { + continue; + } + + // Check if the exit node has access to the org that owns this domain + if (cert.orgId) { + const hasAccess = await checkExitNodeOrg( + remoteExitNode.exitNodeId, + cert.orgId + ); + if (hasAccess) { + // checkExitNodeOrg returns true when access is denied + continue; + } + } + + filtered.push(cert); + } + + const encryptionKeyPath = + config.getRawPrivateConfig().server.encryption_key_path; + + if (!fs.existsSync(encryptionKeyPath)) { + throw new Error( + "Encryption key file not found. Please generate one first." + ); + } + + const encryptionKeyHex = fs + .readFileSync(encryptionKeyPath, "utf8") + .trim(); + const encryptionKey = Buffer.from(encryptionKeyHex, "hex"); + + const result = filtered.map((cert) => { + // Decrypt and save certificate file + const decryptedCert = decryptData( + cert.certFile!, // is not null from query + encryptionKey + ); + + // Decrypt and save key file + const decryptedKey = decryptData(cert.keyFile!, encryptionKey); + + // Return only the certificate data without org information + return { + id: cert.id, + domain: cert.domain, + certFile: decryptedCert, + keyFile: decryptedKey, + expiresAt: cert.expiresAt, + updatedAt: cert.updatedAt, + wildcard: cert.wildcard + }; + }); + + return response(res, { + data: result, + success: true, + error: false, + message: "Certificates retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get certificates for domains" + ) + ); + } + } +); + +// Get resource by domain with pincode and password information +hybridRouter.get( + "/resource/domain/:domain", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getResourceByDomainParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { domain } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [result] = await db + .select() + .from(resources) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId) + ) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId) + ) + .where(eq(resources.fullDomain, domain)) + .limit(1); + + if ( + await checkExitNodeOrg( + remoteExitNode.exitNodeId, + result.resources.orgId + ) + ) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + if (!result) { + return response(res, { + data: null, + success: true, + error: false, + message: "Resource not found", + status: HttpCode.OK + }); + } + + const resourceWithAuth: ResourceWithAuth = { + resource: result.resources, + pincode: result.resourcePincode, + password: result.resourcePassword + }; + + return response(res, { + data: resourceWithAuth, + success: true, + error: false, + message: "Resource retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get resource by domain" + ) + ); + } + } +); + +const getOrgLoginPageParamsSchema = z + .object({ + orgId: z.string().min(1) + }) + .strict(); + +hybridRouter.get( + "/org/:orgId/login-page", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getOrgLoginPageParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [result] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)) + .innerJoin( + loginPage, + eq(loginPageOrg.loginPageId, loginPage.loginPageId) + ) + .limit(1); + + if ( + await checkExitNodeOrg( + remoteExitNode.exitNodeId, + result.loginPageOrg.orgId + ) + ) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + if (!result) { + return response(res, { + data: null, + success: true, + error: false, + message: "Login page not found", + status: HttpCode.OK + }); + } + + return response(res, { + data: result.loginPage, + success: true, + error: false, + message: "Login page retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get org login page" + ) + ); + } + } +); + +// Get user session with user information +hybridRouter.get( + "/session/:userSessionId", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getUserSessionParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { userSessionId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [res_data] = await db + .select() + .from(sessions) + .leftJoin(users, eq(users.userId, sessions.userId)) + .where(eq(sessions.sessionId, userSessionId)); + + if (!res_data) { + return response(res, { + data: null, + success: true, + error: false, + message: "Session not found", + status: HttpCode.OK + }); + } + + // TODO: THIS SEEMS TO BE TERRIBLY INEFFICIENT AND WE CAN FIX WITH SOME KIND OF BETTER SCHEMA!!!!!!!!!!!!!!! + // Check if the user belongs to any organization that the exit node has access to + if (res_data.user) { + const userOrgsResult = await db + .select({ + orgId: userOrgs.orgId + }) + .from(userOrgs) + .where(eq(userOrgs.userId, res_data.user.userId)); + + // Check if the exit node has access to any of the user's organizations + let hasAccess = false; + for (const userOrg of userOrgsResult) { + const accessDenied = await checkExitNodeOrg( + remoteExitNode.exitNodeId, + userOrg.orgId + ); + if (!accessDenied) { + // checkExitNodeOrg returns true when access is denied, false when allowed + hasAccess = true; + break; + } + } + + if (!hasAccess) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not authorized to access this user session" + ) + ); + } + } + + const userSessionWithUser: UserSessionWithUser = { + session: res_data.session, + user: res_data.user + }; + + return response(res, { + data: userSessionWithUser, + success: true, + error: false, + message: "Session retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get user session" + ) + ); + } + } +); + +// Get user organization role +hybridRouter.get( + "/user/:userId/org/:orgId/role", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getUserOrgRoleParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { userId, orgId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + if (await checkExitNodeOrg(remoteExitNode.exitNodeId, orgId)) { + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "User is not authorized to access this organization" + ) + ); + } + + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)) + ) + .limit(1); + + const result = userOrgRole.length > 0 ? userOrgRole[0] : null; + + return response(res, { + data: result, + success: true, + error: false, + message: result + ? "User org role retrieved successfully" + : "User org role not found", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get user org role" + ) + ); + } + } +); + +// Check if role has access to resource +hybridRouter.get( + "/role/:roleId/resource/:resourceId/access", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getRoleResourceAccessParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { roleId, resourceId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [resource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if ( + await checkExitNodeOrg( + remoteExitNode.exitNodeId, + resource.orgId + ) + ) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + const roleResourceAccess = await db + .select() + .from(roleResources) + .where( + and( + eq(roleResources.resourceId, resourceId), + eq(roleResources.roleId, roleId) + ) + ) + .limit(1); + + const result = + roleResourceAccess.length > 0 ? roleResourceAccess[0] : null; + + return response(res, { + data: result, + success: true, + error: false, + message: result + ? "Role resource access retrieved successfully" + : "Role resource access not found", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get role resource access" + ) + ); + } + } +); + +// Check if user has direct access to resource +hybridRouter.get( + "/user/:userId/resource/:resourceId/access", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getUserResourceAccessParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { userId, resourceId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [resource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if ( + await checkExitNodeOrg( + remoteExitNode.exitNodeId, + resource.orgId + ) + ) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + const userResourceAccess = await db + .select() + .from(userResources) + .where( + and( + eq(userResources.userId, userId), + eq(userResources.resourceId, resourceId) + ) + ) + .limit(1); + + const result = + userResourceAccess.length > 0 ? userResourceAccess[0] : null; + + return response(res, { + data: result, + success: true, + error: false, + message: result + ? "User resource access retrieved successfully" + : "User resource access not found", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get user resource access" + ) + ); + } + } +); + +// Get resource rules for a given resource +hybridRouter.get( + "/resource/:resourceId/rules", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getResourceRulesParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { resourceId } = parsedParams.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [resource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if ( + await checkExitNodeOrg( + remoteExitNode.exitNodeId, + resource.orgId + ) + ) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + const rules = await db + .select() + .from(resourceRules) + .where(eq(resourceRules.resourceId, resourceId)); + + return response<(typeof resourceRules.$inferSelect)[]>(res, { + data: rules, + success: true, + error: false, + message: "Resource rules retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get resource rules" + ) + ); + } + } +); + +// Validate resource session token +hybridRouter.post( + "/resource/:resourceId/session/validate", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = + validateResourceSessionTokenParamsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = validateResourceSessionTokenBodySchema.safeParse( + req.body + ); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { resourceId } = parsedParams.data; + const { token } = parsedBody.data; + + const result = await validateResourceSessionToken( + token, + resourceId + ); + + return response(res, { + data: result, + success: true, + error: false, + message: result.resourceSession + ? "Resource session token is valid" + : "Resource session token is invalid or expired", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to validate resource session token" + ) + ); + } + } +); + +const geoIpLookupParamsSchema = z.object({ + ip: z.string().ip() +}); +hybridRouter.get( + "/geoip/:ip", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = geoIpLookupParamsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { ip } = parsedParams.data; + + if (!maxmindLookup) { + return next( + createHttpError( + HttpCode.SERVICE_UNAVAILABLE, + "GeoIP service is not available" + ) + ); + } + + const result = maxmindLookup.get(ip); + + if (!result || !result.country) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "GeoIP information not found" + ) + ); + } + + const { country } = result; + + logger.debug( + `GeoIP lookup successful for IP ${ip}: ${country.iso_code}` + ); + + return response(res, { + data: { countryCode: country.iso_code }, + success: true, + error: false, + message: "GeoIP lookup successful", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to validate resource session token" + ) + ); + } + } +); + +// GERBIL ROUTERS +const getConfigSchema = z.object({ + publicKey: z.string(), + endpoint: z.string(), + listenPort: z.number() +}); +hybridRouter.post( + "/gerbil/get-config", + async (req: Request, res: Response, next: NextFunction) => { + try { + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Exit node not found") + ); + } + + const parsedParams = getConfigSchema.safeParse(req.body); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { publicKey, endpoint, listenPort } = parsedParams.data; + + // update the public key + await db + .update(exitNodes) + .set({ + publicKey: publicKey, + endpoint: endpoint, + listenPort: listenPort + }) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + + const configResponse = await generateGerbilConfig(exitNode); + + return res.status(HttpCode.OK).send(configResponse); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get gerbil config" + ) + ); + } + } +); + +hybridRouter.post( + "/gerbil/receive-bandwidth", + async (req: Request, res: Response, next: NextFunction) => { + try { + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const bandwidthData: any[] = req.body; + + if (!Array.isArray(bandwidthData)) { + throw new Error("Invalid bandwidth data"); + } + + await updateSiteBandwidth( + bandwidthData, + false, + remoteExitNode.exitNodeId + ); // we dont want to check limits + + return res.status(HttpCode.OK).send({ success: true }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to receive bandwidth data" + ) + ); + } + } +); + +const updateHolePunchSchema = z.object({ + olmId: z.string().optional(), + newtId: z.string().optional(), + token: z.string(), + ip: z.string(), + port: z.number(), + timestamp: z.number(), + reachableAt: z.string().optional(), + publicKey: z.string().optional() +}); +hybridRouter.post( + "/gerbil/update-hole-punch", + async (req: Request, res: Response, next: NextFunction) => { + try { + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Exit node not found") + ); + } + + // Validate request parameters + const parsedParams = updateHolePunchSchema.safeParse(req.body); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { olmId, newtId, ip, port, timestamp, token, reachableAt } = + parsedParams.data; + + const destinations = await updateAndGenerateEndpointDestinations( + olmId, + newtId, + ip, + port, + timestamp, + token, + exitNode, + true + ); + + return res.status(HttpCode.OK).send({ + destinations: destinations + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); + } + } +); + +hybridRouter.post( + "/gerbil/get-all-relays", + async (req: Request, res: Response, next: NextFunction) => { + try { + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Exit node not found") + ); + } + + const mappings = await generateRelayMappings(exitNode); + + logger.debug( + `Returning mappings for ${Object.keys(mappings).length} endpoints` + ); + return res.status(HttpCode.OK).send({ mappings }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); + } + } +); + +hybridRouter.post("/badger/exchange-session", exchangeSession); + +const getResolvedHostnameSchema = z.object({ + hostname: z.string(), + publicKey: z.string() +}); + +hybridRouter.post( + "/gerbil/get-resolved-hostname", + async (req: Request, res: Response, next: NextFunction) => { + try { + // Validate request parameters + const parsedParams = getResolvedHostnameSchema.safeParse(req.body); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { hostname, publicKey } = parsedParams.data; + + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode || !remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Exit node not found") + ); + } + + const resourceExitNodes = await resolveExitNodes( + hostname, + publicKey + ); + + if (resourceExitNodes.length === 0) { + return res.status(HttpCode.OK).send({ endpoints: [] }); + } + + // Filter endpoints based on exit node authorization + // WE DONT WANT SOMEONE TO SEND A REQUEST TO SOMEONE'S + // EXIT NODE AND TO FORWARD IT TO ANOTHER'S! + const authorizedEndpoints = []; + for (const node of resourceExitNodes) { + const accessDenied = await checkExitNodeOrg( + remoteExitNode.exitNodeId, + node.orgId + ); + if (!accessDenied) { + // checkExitNodeOrg returns true when access is denied, false when allowed + authorizedEndpoints.push(node.endpoint); + } + } + + if (authorizedEndpoints.length === 0) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not authorized to access this resource" + ) + ); + } + + const endpoints = authorizedEndpoints; + + logger.debug( + `Returning ${Object.keys(endpoints).length} endpoints: ${JSON.stringify(endpoints)}` + ); + return res.status(HttpCode.OK).send({ endpoints }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); + } + } +); + +export default hybridRouter; diff --git a/server/routers/private/loginPage/createLoginPage.ts b/server/routers/private/loginPage/createLoginPage.ts new file mode 100644 index 00000000..fca29aae --- /dev/null +++ b/server/routers/private/loginPage/createLoginPage.ts @@ -0,0 +1,225 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { + db, + exitNodes, + loginPage, + LoginPage, + loginPageOrg, + resources, + sites +} 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 { eq, and } from "drizzle-orm"; +import { validateAndConstructDomain } from "@server/lib/domainUtils"; +import { createCertificate } from "@server/routers/private/certificates/createCertificate"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; +import { build } from "@server/build"; + +const paramsSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +const bodySchema = z + .object({ + subdomain: z.string().nullable().optional(), + domainId: z.string() + }) + .strict(); + +export type CreateLoginPageBody = z.infer; + +export type CreateLoginPageResponse = LoginPage; + +export async function createLoginPage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedBody = bodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { domainId, subdomain } = parsedBody.data; + + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + if (build === "saas") { + const { tier } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + + const [existing] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)); + + if (existing) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "A login page already exists for this organization" + ) + ); + } + + const domainResult = await validateAndConstructDomain( + domainId, + orgId, + subdomain + ); + + if (!domainResult.success) { + return next( + createHttpError(HttpCode.BAD_REQUEST, domainResult.error) + ); + } + + const { fullDomain, subdomain: finalSubdomain } = domainResult; + + logger.debug(`Full domain: ${fullDomain}`); + + const existingResource = await db + .select() + .from(resources) + .where(eq(resources.fullDomain, fullDomain)); + + if (existingResource.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Resource with that domain already exists" + ) + ); + } + + const existingLoginPages = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, fullDomain)); + + if (existingLoginPages.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Login page with that domain already exists" + ) + ); + } + + let returned: LoginPage | undefined; + await db.transaction(async (trx) => { + + const orgSites = await trx + .select() + .from(sites) + .innerJoin(exitNodes, eq(exitNodes.exitNodeId, sites.exitNodeId)) + .where(and(eq(sites.orgId, orgId), eq(exitNodes.type, "gerbil"), eq(exitNodes.online, true))) + .limit(10); + + let exitNodesList = orgSites.map((s) => s.exitNodes); + + if (exitNodesList.length === 0) { + exitNodesList = await trx + .select() + .from(exitNodes) + .where(and(eq(exitNodes.type, "gerbil"), eq(exitNodes.online, true))) + .limit(10); + } + + // select a random exit node + const randomExitNode = + exitNodesList[Math.floor(Math.random() * exitNodesList.length)]; + + if (!randomExitNode) { + throw new Error("No exit nodes available"); + } + + const [returnedLoginPage] = await db + .insert(loginPage) + .values({ + subdomain: finalSubdomain, + fullDomain, + domainId, + exitNodeId: randomExitNode.exitNodeId + }) + .returning(); + + await trx.insert(loginPageOrg).values({ + orgId, + loginPageId: returnedLoginPage.loginPageId + }); + + returned = returnedLoginPage; + }); + + if (!returned) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to create login page" + ) + ); + } + + await createCertificate(domainId, fullDomain, db); + + return response(res, { + data: returned, + success: true, + error: false, + message: "Login page created successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/loginPage/deleteLoginPage.ts b/server/routers/private/loginPage/deleteLoginPage.ts new file mode 100644 index 00000000..7cc957a2 --- /dev/null +++ b/server/routers/private/loginPage/deleteLoginPage.ts @@ -0,0 +1,106 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, loginPage, LoginPage, loginPageOrg } 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 { eq, and } from "drizzle-orm"; + +const paramsSchema = z + .object({ + orgId: z.string(), + loginPageId: z.coerce.number() + }) + .strict(); + +export type DeleteLoginPageResponse = LoginPage; + +export async function deleteLoginPage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const [existingLoginPage] = await db + .select() + .from(loginPage) + .where(eq(loginPage.loginPageId, parsedParams.data.loginPageId)) + .innerJoin( + loginPageOrg, + eq(loginPageOrg.orgId, parsedParams.data.orgId) + ); + + if (!existingLoginPage) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Login page not found") + ); + } + + await db + .delete(loginPageOrg) + .where( + and( + eq(loginPageOrg.orgId, parsedParams.data.orgId), + eq(loginPageOrg.loginPageId, parsedParams.data.loginPageId) + ) + ); + + // const leftoverLinks = await db + // .select() + // .from(loginPageOrg) + // .where(eq(loginPageOrg.loginPageId, parsedParams.data.loginPageId)) + // .limit(1); + + // if (!leftoverLinks.length) { + await db + .delete(loginPage) + .where( + eq(loginPage.loginPageId, parsedParams.data.loginPageId) + ); + + await db + .delete(loginPageOrg) + .where( + eq(loginPageOrg.loginPageId, parsedParams.data.loginPageId) + ); + // } + + return response(res, { + data: existingLoginPage.loginPage, + success: true, + error: false, + message: "Login page deleted successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/loginPage/getLoginPage.ts b/server/routers/private/loginPage/getLoginPage.ts new file mode 100644 index 00000000..9c9a18f5 --- /dev/null +++ b/server/routers/private/loginPage/getLoginPage.ts @@ -0,0 +1,86 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, loginPage, loginPageOrg } from "@server/db"; +import { eq, and } from "drizzle-orm"; +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"; + +const paramsSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +async function query(orgId: string) { + const [res] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)) + .innerJoin( + loginPage, + eq(loginPage.loginPageId, loginPageOrg.loginPageId) + ) + .limit(1); + return res?.loginPage; +} + +export type GetLoginPageResponse = NonNullable< + Awaited> +>; + +export async function getLoginPage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + const loginPage = await query(orgId); + + if (!loginPage) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Login page not found") + ); + } + + return response(res, { + data: loginPage, + success: true, + error: false, + message: "Login page retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/loginPage/index.ts b/server/routers/private/loginPage/index.ts new file mode 100644 index 00000000..2372ddfa --- /dev/null +++ b/server/routers/private/loginPage/index.ts @@ -0,0 +1,19 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./createLoginPage"; +export * from "./updateLoginPage"; +export * from "./getLoginPage"; +export * from "./loadLoginPage"; +export * from "./updateLoginPage"; +export * from "./deleteLoginPage"; diff --git a/server/routers/private/loginPage/loadLoginPage.ts b/server/routers/private/loginPage/loadLoginPage.ts new file mode 100644 index 00000000..91cc002e --- /dev/null +++ b/server/routers/private/loginPage/loadLoginPage.ts @@ -0,0 +1,148 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, idpOrg, loginPage, loginPageOrg, resources } from "@server/db"; +import { eq, and } from "drizzle-orm"; +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"; + +const querySchema = z.object({ + resourceId: z.coerce.number().int().positive().optional(), + idpId: z.coerce.number().int().positive().optional(), + orgId: z.coerce.number().int().positive().optional(), + fullDomain: z.string().min(1) +}); + +async function query(orgId: string | undefined, fullDomain: string) { + if (!orgId) { + const [res] = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, fullDomain)) + .innerJoin( + loginPageOrg, + eq(loginPage.loginPageId, loginPageOrg.loginPageId) + ) + .limit(1); + return { + ...res.loginPage, + orgId: res.loginPageOrg.orgId + }; + } + + const [orgLink] = await db + .select() + .from(loginPageOrg) + .where(eq(loginPageOrg.orgId, orgId)); + + if (!orgLink) { + return null; + } + + const [res] = await db + .select() + .from(loginPage) + .where( + and( + eq(loginPage.loginPageId, orgLink.loginPageId), + eq(loginPage.fullDomain, fullDomain) + ) + ) + .limit(1); + return { + ...res, + orgId: orgLink.orgId + }; +} + +export type LoadLoginPageResponse = NonNullable< + Awaited> +> & { orgId: string }; + +export async function loadLoginPage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + + const { resourceId, idpId, fullDomain } = parsedQuery.data; + + let orgId; + if (resourceId) { + const [resource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if (!resource) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Resource not found") + ); + } + + orgId = resource.orgId; + } else if (idpId) { + const [idpOrgLink] = await db + .select() + .from(idpOrg) + .where(eq(idpOrg.idpId, idpId)); + + if (!idpOrgLink) { + return next( + createHttpError(HttpCode.NOT_FOUND, "IdP not found") + ); + } + + orgId = idpOrgLink.orgId; + } else if (parsedQuery.data.orgId) { + orgId = parsedQuery.data.orgId.toString(); + } + + const loginPage = await query(orgId, fullDomain); + + if (!loginPage) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Login page not found") + ); + } + + return response(res, { + data: loginPage, + success: true, + error: false, + message: "Login page retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/loginPage/updateLoginPage.ts b/server/routers/private/loginPage/updateLoginPage.ts new file mode 100644 index 00000000..9c19913d --- /dev/null +++ b/server/routers/private/loginPage/updateLoginPage.ts @@ -0,0 +1,227 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, loginPage, LoginPage, loginPageOrg, resources } 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 { eq, and } from "drizzle-orm"; +import { validateAndConstructDomain } from "@server/lib/domainUtils"; +import { subdomainSchema } from "@server/lib/schemas"; +import { createCertificate } from "@server/routers/private/certificates/createCertificate"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; +import { build } from "@server/build"; + +const paramsSchema = z + .object({ + orgId: z.string(), + loginPageId: z.coerce.number() + }) + .strict(); + +const bodySchema = z + .object({ + subdomain: subdomainSchema.nullable().optional(), + domainId: z.string().optional() + }) + .strict() + .refine((data) => Object.keys(data).length > 0, { + message: "At least one field must be provided for update" + }) + .refine( + (data) => { + if (data.subdomain) { + return subdomainSchema.safeParse(data.subdomain).success; + } + return true; + }, + { message: "Invalid subdomain" } + ); + +export type UpdateLoginPageBody = z.infer; + +export type UpdateLoginPageResponse = LoginPage; + +export async function updateLoginPage( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedBody = bodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const updateData = parsedBody.data; + + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { loginPageId, orgId } = parsedParams.data; + + if (build === "saas"){ + const { tier } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + + const [existingLoginPage] = await db + .select() + .from(loginPage) + .where(eq(loginPage.loginPageId, loginPageId)); + + if (!existingLoginPage) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Login page not found") + ); + } + + const [orgLink] = await db + .select() + .from(loginPageOrg) + .where( + and( + eq(loginPageOrg.orgId, orgId), + eq(loginPageOrg.loginPageId, loginPageId) + ) + ); + + if (!orgLink) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "Login page not found for this organization" + ) + ); + } + + if (updateData.domainId) { + const domainId = updateData.domainId; + + // Validate domain and construct full domain + const domainResult = await validateAndConstructDomain( + domainId, + orgId, + updateData.subdomain + ); + + if (!domainResult.success) { + return next( + createHttpError(HttpCode.BAD_REQUEST, domainResult.error) + ); + } + + const { fullDomain, subdomain: finalSubdomain } = domainResult; + + logger.debug(`Full domain: ${fullDomain}`); + + if (fullDomain) { + const [existingDomain] = await db + .select() + .from(resources) + .where(eq(resources.fullDomain, fullDomain)); + + if (existingDomain) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Resource with that domain already exists" + ) + ); + } + + const [existingLoginPage] = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, fullDomain)); + + if ( + existingLoginPage && + existingLoginPage.loginPageId !== loginPageId + ) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Login page with that domain already exists" + ) + ); + } + + // update the full domain if it has changed + if (fullDomain && fullDomain !== existingLoginPage?.fullDomain) { + await db + .update(loginPage) + .set({ fullDomain }) + .where(eq(loginPage.loginPageId, loginPageId)); + } + + await createCertificate(domainId, fullDomain, db); + } + + updateData.subdomain = finalSubdomain; + } + + const updatedLoginPage = await db + .update(loginPage) + .set({ ...updateData }) + .where(eq(loginPage.loginPageId, loginPageId)) + .returning(); + + if (updatedLoginPage.length === 0) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Login page with ID ${loginPageId} not found` + ) + ); + } + + return response(res, { + data: updatedLoginPage[0], + success: true, + error: false, + message: "Login page created successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/createOrgOidcIdp.ts b/server/routers/private/orgIdp/createOrgOidcIdp.ts new file mode 100644 index 00000000..16697f98 --- /dev/null +++ b/server/routers/private/orgIdp/createOrgOidcIdp.ts @@ -0,0 +1,185 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { 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 { idp, idpOidcConfig, idpOrg, orgs } from "@server/db"; +import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; +import { encrypt } from "@server/lib/crypto"; +import config from "@server/lib/config"; +import { build } from "@server/build"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; + +const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict(); + +const bodySchema = z + .object({ + name: z.string().nonempty(), + clientId: z.string().nonempty(), + clientSecret: z.string().nonempty(), + authUrl: z.string().url(), + tokenUrl: z.string().url(), + identifierPath: z.string().nonempty(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().nonempty(), + autoProvision: z.boolean().optional(), + variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"), + roleMapping: z.string().optional() + }) + .strict(); + +export type CreateOrgIdpResponse = { + idpId: number; + redirectUrl: string; +}; + +// registry.registerPath({ +// method: "put", +// path: "/idp/oidc", +// description: "Create an OIDC IdP.", +// tags: [OpenAPITags.Idp], +// request: { +// body: { +// content: { +// "application/json": { +// schema: bodySchema +// } +// } +// } +// }, +// responses: {} +// }); + +export async function createOrgOidcIdp( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + const parsedBody = bodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { + clientId, + clientSecret, + authUrl, + tokenUrl, + scopes, + identifierPath, + emailPath, + namePath, + name, + autoProvision, + variant, + roleMapping + } = parsedBody.data; + + if (build === "saas") { + const { tier, active } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + + const key = config.getRawConfig().server.secret!; + + const encryptedSecret = encrypt(clientSecret, key); + const encryptedClientId = encrypt(clientId, key); + + let idpId: number | undefined; + await db.transaction(async (trx) => { + const [idpRes] = await trx + .insert(idp) + .values({ + name, + autoProvision, + type: "oidc" + }) + .returning(); + + idpId = idpRes.idpId; + + await trx.insert(idpOidcConfig).values({ + idpId: idpRes.idpId, + clientId: encryptedClientId, + clientSecret: encryptedSecret, + authUrl, + tokenUrl, + scopes, + identifierPath, + emailPath, + namePath, + variant + }); + + await trx.insert(idpOrg).values({ + idpId: idpRes.idpId, + orgId: orgId, + roleMapping: roleMapping || null, + orgMapping: `'${orgId}'` + }); + }); + + const redirectUrl = await generateOidcRedirectUrl(idpId as number, orgId); + + return response(res, { + data: { + idpId: idpId as number, + redirectUrl + }, + success: true, + error: false, + message: "Org Idp created successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/getOrgIdp.ts b/server/routers/private/orgIdp/getOrgIdp.ts new file mode 100644 index 00000000..73ccdcbb --- /dev/null +++ b/server/routers/private/orgIdp/getOrgIdp.ts @@ -0,0 +1,117 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, idpOrg, loginPage, loginPageOrg } from "@server/db"; +import { idp, idpOidcConfig } from "@server/db"; +import { eq, and } from "drizzle-orm"; +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 config from "@server/lib/config"; +import { decrypt } from "@server/lib/crypto"; +import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; + +const paramsSchema = z + .object({ + orgId: z.string().nonempty(), + idpId: z.coerce.number() + }) + .strict(); + +async function query(idpId: number, orgId: string) { + const [res] = await db + .select() + .from(idp) + .where(eq(idp.idpId, idpId)) + .leftJoin(idpOidcConfig, eq(idpOidcConfig.idpId, idp.idpId)) + .leftJoin( + idpOrg, + and(eq(idpOrg.idpId, idp.idpId), eq(idpOrg.orgId, orgId)) + ) + .limit(1); + return res; +} + +export type GetOrgIdpResponse = NonNullable< + Awaited> +> & { redirectUrl: string }; + +// registry.registerPath({ +// method: "get", +// path: "/idp/{idpId}", +// description: "Get an IDP by its IDP ID.", +// tags: [OpenAPITags.Idp], +// request: { +// params: paramsSchema +// }, +// responses: {} +// }); + +export async function getOrgIdp( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { idpId, orgId } = parsedParams.data; + + const idpRes = await query(idpId, orgId); + + if (!idpRes) { + return next(createHttpError(HttpCode.NOT_FOUND, "Idp not found")); + } + + const key = config.getRawConfig().server.secret!; + + if (idpRes.idp.type === "oidc") { + const clientSecret = idpRes.idpOidcConfig!.clientSecret; + const clientId = idpRes.idpOidcConfig!.clientId; + + idpRes.idpOidcConfig!.clientSecret = decrypt(clientSecret, key); + idpRes.idpOidcConfig!.clientId = decrypt(clientId, key); + } + + const redirectUrl = await generateOidcRedirectUrl(idpRes.idp.idpId, orgId); + + return response(res, { + data: { + ...idpRes, + redirectUrl + }, + success: true, + error: false, + message: "Org Idp retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/index.ts b/server/routers/private/orgIdp/index.ts new file mode 100644 index 00000000..99c30654 --- /dev/null +++ b/server/routers/private/orgIdp/index.ts @@ -0,0 +1,17 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./createOrgOidcIdp"; +export * from "./getOrgIdp"; +export * from "./listOrgIdps"; +export * from "./updateOrgOidcIdp"; \ No newline at end of file diff --git a/server/routers/private/orgIdp/listOrgIdps.ts b/server/routers/private/orgIdp/listOrgIdps.ts new file mode 100644 index 00000000..208732de --- /dev/null +++ b/server/routers/private/orgIdp/listOrgIdps.ts @@ -0,0 +1,142 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, idpOidcConfig } from "@server/db"; +import { idp, idpOrg } from "@server/db"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import { eq, sql } from "drizzle-orm"; +import logger from "@server/logger"; +import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; + +const querySchema = z + .object({ + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.number().int().nonnegative()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.number().int().nonnegative()) + }) + .strict(); + +const paramsSchema = z + .object({ + orgId: z.string().nonempty() + }) + .strict(); + +async function query(orgId: string, limit: number, offset: number) { + const res = await db + .select({ + idpId: idp.idpId, + orgId: idpOrg.orgId, + name: idp.name, + type: idp.type, + variant: idpOidcConfig.variant + }) + .from(idpOrg) + .where(eq(idpOrg.orgId, orgId)) + .innerJoin(idp, eq(idp.idpId, idpOrg.idpId)) + .innerJoin(idpOidcConfig, eq(idpOidcConfig.idpId, idpOrg.idpId)) + .orderBy(sql`idp.name DESC`) + .limit(limit) + .offset(offset); + return res; +} + +export type ListOrgIdpsResponse = { + idps: Awaited>; + pagination: { + total: number; + limit: number; + offset: number; + }; +}; + +// registry.registerPath({ +// method: "get", +// path: "/idp", +// description: "List all IDP in the system.", +// tags: [OpenAPITags.Idp], +// request: { +// query: querySchema +// }, +// responses: {} +// }); + +export async function listOrgIdps( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + const { orgId } = parsedParams.data; + + const parsedQuery = querySchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error).toString() + ) + ); + } + const { limit, offset } = parsedQuery.data; + + const list = await query(orgId, limit, offset); + + const [{ count }] = await db + .select({ count: sql`count(*)` }) + .from(idp); + + return response(res, { + data: { + idps: list, + pagination: { + total: count, + limit, + offset + } + }, + success: true, + error: false, + message: "Org Idps retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/updateOrgOidcIdp.ts b/server/routers/private/orgIdp/updateOrgOidcIdp.ts new file mode 100644 index 00000000..a3be85c3 --- /dev/null +++ b/server/routers/private/orgIdp/updateOrgOidcIdp.ts @@ -0,0 +1,237 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, idpOrg } 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 { idp, idpOidcConfig } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import { encrypt } from "@server/lib/crypto"; +import config from "@server/lib/config"; +import license from "@server/license/license"; +import { build } from "@server/build"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; + +const paramsSchema = z + .object({ + orgId: z.string().nonempty(), + idpId: z.coerce.number() + }) + .strict(); + +const bodySchema = z + .object({ + name: z.string().optional(), + clientId: z.string().optional(), + clientSecret: z.string().optional(), + authUrl: z.string().optional(), + tokenUrl: z.string().optional(), + identifierPath: z.string().optional(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z.string().optional(), + autoProvision: z.boolean().optional(), + roleMapping: z.string().optional() + }) + .strict(); + +export type UpdateOrgIdpResponse = { + idpId: number; +}; + +// registry.registerPath({ +// method: "post", +// path: "/idp/{idpId}/oidc", +// description: "Update an OIDC IdP.", +// tags: [OpenAPITags.Idp], +// request: { +// params: paramsSchema, +// body: { +// content: { +// "application/json": { +// schema: bodySchema +// } +// } +// } +// }, +// responses: {} +// }); + +export async function updateOrgOidcIdp( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = bodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { idpId, orgId } = parsedParams.data; + const { + clientId, + clientSecret, + authUrl, + tokenUrl, + scopes, + identifierPath, + emailPath, + namePath, + name, + autoProvision, + roleMapping + } = parsedBody.data; + + if (build === "saas") { + const { tier, active } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + + // Check if IDP exists and is of type OIDC + const [existingIdp] = await db + .select() + .from(idp) + .where(eq(idp.idpId, idpId)); + + if (!existingIdp) { + return next(createHttpError(HttpCode.NOT_FOUND, "IdP not found")); + } + + const [existingIdpOrg] = await db + .select() + .from(idpOrg) + .where(and(eq(idpOrg.orgId, orgId), eq(idpOrg.idpId, idpId))); + + if (!existingIdpOrg) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "IdP not found for this organization" + ) + ); + } + + if (existingIdp.type !== "oidc") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "IdP is not an OIDC provider" + ) + ); + } + + const key = config.getRawConfig().server.secret!; + const encryptedSecret = clientSecret + ? encrypt(clientSecret, key) + : undefined; + const encryptedClientId = clientId ? encrypt(clientId, key) : undefined; + + await db.transaction(async (trx) => { + const idpData = { + name, + autoProvision + }; + + // only update if at least one key is not undefined + let keysToUpdate = Object.keys(idpData).filter( + (key) => idpData[key as keyof typeof idpData] !== undefined + ); + + if (keysToUpdate.length > 0) { + await trx.update(idp).set(idpData).where(eq(idp.idpId, idpId)); + } + + const configData = { + clientId: encryptedClientId, + clientSecret: encryptedSecret, + authUrl, + tokenUrl, + scopes, + identifierPath, + emailPath, + namePath + }; + + keysToUpdate = Object.keys(configData).filter( + (key) => + configData[key as keyof typeof configData] !== undefined + ); + + if (keysToUpdate.length > 0) { + // Update OIDC config + await trx + .update(idpOidcConfig) + .set(configData) + .where(eq(idpOidcConfig.idpId, idpId)); + } + + if (roleMapping !== undefined) { + // Update IdP-org policy + await trx + .update(idpOrg) + .set({ + roleMapping + }) + .where( + and(eq(idpOrg.idpId, idpId), eq(idpOrg.orgId, orgId)) + ); + } + }); + + return response(res, { + data: { + idpId + }, + success: true, + error: false, + message: "Org IdP updated successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/remoteExitNode/createRemoteExitNode.ts b/server/routers/private/remoteExitNode/createRemoteExitNode.ts new file mode 100644 index 00000000..ac4fd231 --- /dev/null +++ b/server/routers/private/remoteExitNode/createRemoteExitNode.ts @@ -0,0 +1,278 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { db, exitNodes, exitNodeOrgs, ExitNode, ExitNodeOrg } from "@server/db"; +import HttpCode from "@server/types/HttpCode"; +import { z } from "zod"; +import { remoteExitNodes } from "@server/db"; +import createHttpError from "http-errors"; +import response from "@server/lib/response"; +import { SqliteError } from "better-sqlite3"; +import moment from "moment"; +import { generateSessionToken } from "@server/auth/sessions/app"; +import { createRemoteExitNodeSession } from "@server/auth/sessions/privateRemoteExitNode"; +import { fromError } from "zod-validation-error"; +import { hashPassword, verifyPassword } from "@server/auth/password"; +import logger from "@server/logger"; +import { and, eq } from "drizzle-orm"; +import { getNextAvailableSubnet } from "@server/lib/exitNodes"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; + +export const paramsSchema = z.object({ + orgId: z.string() +}); + +export type CreateRemoteExitNodeResponse = { + token: string; + remoteExitNodeId: string; + secret: string; +}; + +const bodySchema = z + .object({ + remoteExitNodeId: z.string().length(15), + secret: z.string().length(48) + }) + .strict(); + +export type CreateRemoteExitNodeBody = z.infer; + +export async function createRemoteExitNode( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + const parsedBody = bodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { remoteExitNodeId, secret } = parsedBody.data; + + if (req.user && !req.userOrgRoleId) { + return next( + createHttpError(HttpCode.FORBIDDEN, "User does not have a role") + ); + } + + const usage = await usageService.getUsage( + orgId, + FeatureId.REMOTE_EXIT_NODES + ); + if (!usage) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "No usage data found for this organization" + ) + ); + } + const rejectRemoteExitNodes = await usageService.checkLimitSet( + orgId, + false, + FeatureId.REMOTE_EXIT_NODES, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectRemoteExitNodes) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Remote exit node limit exceeded. Please upgrade your plan or contact us at support@fossorial.io" + ) + ); + } + + const secretHash = await hashPassword(secret); + // const address = await getNextAvailableSubnet(); + const address = "100.89.140.1/24"; // FOR NOW LETS HARDCODE THESE ADDRESSES + + const [existingRemoteExitNode] = await db + .select() + .from(remoteExitNodes) + .where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId)); + + if (existingRemoteExitNode) { + // validate the secret + + const validSecret = await verifyPassword( + secret, + existingRemoteExitNode.secretHash + ); + if (!validSecret) { + logger.info( + `Failed secret validation for remote exit node: ${remoteExitNodeId}` + ); + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "Invalid secret for remote exit node" + ) + ); + } + } + + let existingExitNode: ExitNode | null = null; + if (existingRemoteExitNode?.exitNodeId) { + const [res] = await db + .select() + .from(exitNodes) + .where( + eq(exitNodes.exitNodeId, existingRemoteExitNode.exitNodeId) + ); + existingExitNode = res; + } + + let existingExitNodeOrg: ExitNodeOrg | null = null; + if (existingRemoteExitNode?.exitNodeId) { + const [res] = await db + .select() + .from(exitNodeOrgs) + .where( + and( + eq( + exitNodeOrgs.exitNodeId, + existingRemoteExitNode.exitNodeId + ), + eq(exitNodeOrgs.orgId, orgId) + ) + ); + existingExitNodeOrg = res; + } + + if (existingExitNodeOrg) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node already exists in this organization" + ) + ); + } + + let numExitNodeOrgs: ExitNodeOrg[] | undefined; + + await db.transaction(async (trx) => { + if (!existingExitNode) { + const [res] = await trx + .insert(exitNodes) + .values({ + name: remoteExitNodeId, + address, + endpoint: "", + publicKey: "", + listenPort: 0, + online: false, + type: "remoteExitNode" + }) + .returning(); + existingExitNode = res; + } + + if (!existingRemoteExitNode) { + await trx.insert(remoteExitNodes).values({ + remoteExitNodeId: remoteExitNodeId, + secretHash, + dateCreated: moment().toISOString(), + exitNodeId: existingExitNode.exitNodeId + }); + } else { + // update the existing remote exit node + await trx + .update(remoteExitNodes) + .set({ + exitNodeId: existingExitNode.exitNodeId + }) + .where( + eq( + remoteExitNodes.remoteExitNodeId, + existingRemoteExitNode.remoteExitNodeId + ) + ); + } + + if (!existingExitNodeOrg) { + await trx.insert(exitNodeOrgs).values({ + exitNodeId: existingExitNode.exitNodeId, + orgId: orgId + }); + } + + numExitNodeOrgs = await trx + .select() + .from(exitNodeOrgs) + .where(eq(exitNodeOrgs.orgId, orgId)); + }); + + if (numExitNodeOrgs) { + await usageService.updateDaily( + orgId, + FeatureId.REMOTE_EXIT_NODES, + numExitNodeOrgs.length + ); + } + + const token = generateSessionToken(); + await createRemoteExitNodeSession(token, remoteExitNodeId); + + return response(res, { + data: { + remoteExitNodeId, + secret, + token + }, + success: true, + error: false, + message: "RemoteExitNode created successfully", + status: HttpCode.OK + }); + } catch (e) { + if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "A remote exit node with that ID already exists" + ) + ); + } else { + logger.error("Failed to create remoteExitNode", e); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to create remoteExitNode" + ) + ); + } + } +} diff --git a/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts b/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts new file mode 100644 index 00000000..84ef0fab --- /dev/null +++ b/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts @@ -0,0 +1,131 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { z } from "zod"; +import { db, ExitNodeOrg, exitNodeOrgs, exitNodes } from "@server/db"; +import { remoteExitNodes } from "@server/db"; +import { and, count, eq } from "drizzle-orm"; +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 { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; + +const paramsSchema = z + .object({ + orgId: z.string().min(1), + remoteExitNodeId: z.string().min(1) + }) + .strict(); + +export async function deleteRemoteExitNode( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId, remoteExitNodeId } = parsedParams.data; + + const [remoteExitNode] = await db + .select() + .from(remoteExitNodes) + .where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId)); + + if (!remoteExitNode) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Remote exit node with ID ${remoteExitNodeId} not found` + ) + ); + } + + if (!remoteExitNode.exitNodeId) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `Remote exit node with ID ${remoteExitNodeId} does not have an exit node ID` + ) + ); + } + + let numExitNodeOrgs: ExitNodeOrg[] | undefined; + await db.transaction(async (trx) => { + await trx + .delete(exitNodeOrgs) + .where( + and( + eq(exitNodeOrgs.orgId, orgId), + eq(exitNodeOrgs.exitNodeId, remoteExitNode.exitNodeId!) + ) + ); + + const [remainingExitNodeOrgs] = await trx + .select({ count: count() }) + .from(exitNodeOrgs) + .where(eq(exitNodeOrgs.exitNodeId, remoteExitNode.exitNodeId!)); + + if (remainingExitNodeOrgs.count === 0) { + await trx + .delete(remoteExitNodes) + .where( + eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId) + ); + await trx + .delete(exitNodes) + .where( + eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId!) + ); + } + + numExitNodeOrgs = await trx + .select() + .from(exitNodeOrgs) + .where(eq(exitNodeOrgs.orgId, orgId)); + }); + + if (numExitNodeOrgs) { + await usageService.updateDaily( + orgId, + FeatureId.REMOTE_EXIT_NODES, + numExitNodeOrgs.length + ); + } + + return response(res, { + data: null, + success: true, + error: false, + message: "Remote exit node deleted successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/remoteExitNode/getRemoteExitNode.ts b/server/routers/private/remoteExitNode/getRemoteExitNode.ts new file mode 100644 index 00000000..19c4f263 --- /dev/null +++ b/server/routers/private/remoteExitNode/getRemoteExitNode.ts @@ -0,0 +1,99 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { z } from "zod"; +import { db, exitNodes } from "@server/db"; +import { remoteExitNodes } from "@server/db"; +import { eq } from "drizzle-orm"; +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"; + +const getRemoteExitNodeSchema = z + .object({ + orgId: z.string().min(1), + remoteExitNodeId: z.string().min(1) + }) + .strict(); + +async function query(remoteExitNodeId: string) { + const [remoteExitNode] = await db + .select({ + remoteExitNodeId: remoteExitNodes.remoteExitNodeId, + dateCreated: remoteExitNodes.dateCreated, + version: remoteExitNodes.version, + exitNodeId: remoteExitNodes.exitNodeId, + name: exitNodes.name, + address: exitNodes.address, + endpoint: exitNodes.endpoint, + online: exitNodes.online, + type: exitNodes.type + }) + .from(remoteExitNodes) + .innerJoin( + exitNodes, + eq(exitNodes.exitNodeId, remoteExitNodes.exitNodeId) + ) + .where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId)) + .limit(1); + return remoteExitNode; +} + +export type GetRemoteExitNodeResponse = Awaited>; + +export async function getRemoteExitNode( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = getRemoteExitNodeSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { remoteExitNodeId } = parsedParams.data; + + const remoteExitNode = await query(remoteExitNodeId); + + if (!remoteExitNode) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Remote exit node with ID ${remoteExitNodeId} not found` + ) + ); + } + + return response(res, { + data: remoteExitNode, + success: true, + error: false, + message: "Remote exit node retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts b/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts new file mode 100644 index 00000000..3905f1f7 --- /dev/null +++ b/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts @@ -0,0 +1,130 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { generateSessionToken } from "@server/auth/sessions/app"; +import { db } from "@server/db"; +import { remoteExitNodes } from "@server/db"; +import HttpCode from "@server/types/HttpCode"; +import response from "@server/lib/response"; +import { eq } from "drizzle-orm"; +import { NextFunction, Request, Response } from "express"; +import createHttpError from "http-errors"; +import { z } from "zod"; +import { fromError } from "zod-validation-error"; +import { + createRemoteExitNodeSession, + validateRemoteExitNodeSessionToken +} from "@server/auth/sessions/privateRemoteExitNode"; +import { verifyPassword } from "@server/auth/password"; +import logger from "@server/logger"; +import config from "@server/lib/config"; + +export const remoteExitNodeGetTokenBodySchema = z.object({ + remoteExitNodeId: z.string(), + secret: z.string(), + token: z.string().optional() +}); + +export type RemoteExitNodeGetTokenBody = z.infer; + +export async function getRemoteExitNodeToken( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = remoteExitNodeGetTokenBodySchema.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { remoteExitNodeId, secret, token } = parsedBody.data; + + try { + if (token) { + const { session, remoteExitNode } = await validateRemoteExitNodeSessionToken(token); + if (session) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `RemoteExitNode session already valid. RemoteExitNode ID: ${remoteExitNodeId}. IP: ${req.ip}.` + ); + } + return response(res, { + data: null, + success: true, + error: false, + message: "Token session already valid", + status: HttpCode.OK + }); + } + } + + const existingRemoteExitNodeRes = await db + .select() + .from(remoteExitNodes) + .where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId)); + if (!existingRemoteExitNodeRes || !existingRemoteExitNodeRes.length) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "No remoteExitNode found with that remoteExitNodeId" + ) + ); + } + + const existingRemoteExitNode = existingRemoteExitNodeRes[0]; + + const validSecret = await verifyPassword( + secret, + existingRemoteExitNode.secretHash + ); + if (!validSecret) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `RemoteExitNode id or secret is incorrect. RemoteExitNode: ID ${remoteExitNodeId}. IP: ${req.ip}.` + ); + } + return next( + createHttpError(HttpCode.BAD_REQUEST, "Secret is incorrect") + ); + } + + const resToken = generateSessionToken(); + await createRemoteExitNodeSession(resToken, existingRemoteExitNode.remoteExitNodeId); + + // logger.debug(`Created RemoteExitNode token response: ${JSON.stringify(resToken)}`); + + return response<{ token: string }>(res, { + data: { + token: resToken + }, + success: true, + error: false, + message: "Token created successfully", + status: HttpCode.OK + }); + } catch (e) { + console.error(e); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to authenticate remoteExitNode" + ) + ); + } +} diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts b/server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts new file mode 100644 index 00000000..3e1e130d --- /dev/null +++ b/server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts @@ -0,0 +1,140 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { db, exitNodes, sites } from "@server/db"; +import { MessageHandler } from "@server/routers/ws"; +import { clients, RemoteExitNode } from "@server/db"; +import { eq, lt, isNull, and, or, inArray } from "drizzle-orm"; +import logger from "@server/logger"; + +// Track if the offline checker interval is running +let offlineCheckerInterval: NodeJS.Timeout | null = null; +const OFFLINE_CHECK_INTERVAL = 30 * 1000; // Check every 30 seconds +const OFFLINE_THRESHOLD_MS = 2 * 60 * 1000; // 2 minutes + +/** + * Starts the background interval that checks for clients that haven't pinged recently + * and marks them as offline + */ +export const startRemoteExitNodeOfflineChecker = (): void => { + if (offlineCheckerInterval) { + return; // Already running + } + + offlineCheckerInterval = setInterval(async () => { + try { + const twoMinutesAgo = Math.floor((Date.now() - OFFLINE_THRESHOLD_MS) / 1000); + + // Find clients that haven't pinged in the last 2 minutes and mark them as offline + const newlyOfflineNodes = await db + .update(exitNodes) + .set({ online: false }) + .where( + and( + eq(exitNodes.online, true), + eq(exitNodes.type, "remoteExitNode"), + or( + lt(exitNodes.lastPing, twoMinutesAgo), + isNull(exitNodes.lastPing) + ) + ) + ).returning(); + + + // Update the sites to offline if they have not pinged either + const exitNodeIds = newlyOfflineNodes.map(node => node.exitNodeId); + + const sitesOnNode = await db + .select() + .from(sites) + .where( + and( + eq(sites.online, true), + inArray(sites.exitNodeId, exitNodeIds) + ) + ); + + // loop through the sites and process their lastBandwidthUpdate as an iso string and if its more than 1 minute old then mark the site offline + for (const site of sitesOnNode) { + if (!site.lastBandwidthUpdate) { + continue; + } + const lastBandwidthUpdate = new Date(site.lastBandwidthUpdate); + if (Date.now() - lastBandwidthUpdate.getTime() > 60 * 1000) { + await db + .update(sites) + .set({ online: false }) + .where(eq(sites.siteId, site.siteId)); + } + } + + } catch (error) { + logger.error("Error in offline checker interval", { error }); + } + }, OFFLINE_CHECK_INTERVAL); + + logger.info("Started offline checker interval"); +}; + +/** + * Stops the background interval that checks for offline clients + */ +export const stopRemoteExitNodeOfflineChecker = (): void => { + if (offlineCheckerInterval) { + clearInterval(offlineCheckerInterval); + offlineCheckerInterval = null; + logger.info("Stopped offline checker interval"); + } +}; + +/** + * Handles ping messages from clients and responds with pong + */ +export const handleRemoteExitNodePingMessage: MessageHandler = async (context) => { + const { message, client: c, sendToClient } = context; + const remoteExitNode = c as RemoteExitNode; + + if (!remoteExitNode) { + logger.debug("RemoteExitNode not found"); + return; + } + + if (!remoteExitNode.exitNodeId) { + logger.debug("RemoteExitNode has no exit node ID!"); // this can happen if the exit node is created but not adopted yet + return; + } + + try { + // Update the exit node's last ping timestamp + await db + .update(exitNodes) + .set({ + lastPing: Math.floor(Date.now() / 1000), + online: true, + }) + .where(eq(exitNodes.exitNodeId, remoteExitNode.exitNodeId)); + } catch (error) { + logger.error("Error handling ping message", { error }); + } + + return { + message: { + type: "pong", + data: { + timestamp: new Date().toISOString(), + } + }, + broadcast: false, + excludeSender: false + }; +}; \ No newline at end of file diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts b/server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts new file mode 100644 index 00000000..9e50a841 --- /dev/null +++ b/server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts @@ -0,0 +1,49 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { db, RemoteExitNode, remoteExitNodes } from "@server/db"; +import { MessageHandler } from "@server/routers/ws"; +import { eq } from "drizzle-orm"; +import logger from "@server/logger"; + +export const handleRemoteExitNodeRegisterMessage: MessageHandler = async ( + context +) => { + const { message, client, sendToClient } = context; + const remoteExitNode = client as RemoteExitNode; + + logger.debug("Handling register remoteExitNode message!"); + + if (!remoteExitNode) { + logger.warn("Remote exit node not found"); + return; + } + + const { remoteExitNodeVersion } = message.data; + + if (!remoteExitNodeVersion) { + logger.warn("Remote exit node version not found"); + return; + } + + // update the version + await db + .update(remoteExitNodes) + .set({ version: remoteExitNodeVersion }) + .where( + eq( + remoteExitNodes.remoteExitNodeId, + remoteExitNode.remoteExitNodeId + ) + ); +}; diff --git a/server/routers/private/remoteExitNode/index.ts b/server/routers/private/remoteExitNode/index.ts new file mode 100644 index 00000000..2a04f9d9 --- /dev/null +++ b/server/routers/private/remoteExitNode/index.ts @@ -0,0 +1,23 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./createRemoteExitNode"; +export * from "./getRemoteExitNode"; +export * from "./listRemoteExitNodes"; +export * from "./getRemoteExitNodeToken"; +export * from "./handleRemoteExitNodeRegisterMessage"; +export * from "./handleRemoteExitNodePingMessage"; +export * from "./deleteRemoteExitNode"; +export * from "./listRemoteExitNodes"; +export * from "./pickRemoteExitNodeDefaults"; +export * from "./quickStartRemoteExitNode"; diff --git a/server/routers/private/remoteExitNode/listRemoteExitNodes.ts b/server/routers/private/remoteExitNode/listRemoteExitNodes.ts new file mode 100644 index 00000000..d6d2466e --- /dev/null +++ b/server/routers/private/remoteExitNode/listRemoteExitNodes.ts @@ -0,0 +1,147 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { z } from "zod"; +import { db, exitNodeOrgs, exitNodes } from "@server/db"; +import { remoteExitNodes } from "@server/db"; +import { eq, and, count } from "drizzle-orm"; +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"; + +const listRemoteExitNodesParamsSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +const listRemoteExitNodesSchema = z.object({ + limit: z + .string() + .optional() + .default("1000") + .transform(Number) + .pipe(z.number().int().positive()), + offset: z + .string() + .optional() + .default("0") + .transform(Number) + .pipe(z.number().int().nonnegative()) +}); + +function queryRemoteExitNodes(orgId: string) { + return db + .select({ + remoteExitNodeId: remoteExitNodes.remoteExitNodeId, + dateCreated: remoteExitNodes.dateCreated, + version: remoteExitNodes.version, + exitNodeId: remoteExitNodes.exitNodeId, + name: exitNodes.name, + address: exitNodes.address, + endpoint: exitNodes.endpoint, + online: exitNodes.online, + type: exitNodes.type + }) + .from(exitNodeOrgs) + .where(eq(exitNodeOrgs.orgId, orgId)) + .innerJoin(exitNodes, eq(exitNodes.exitNodeId, exitNodeOrgs.exitNodeId)) + .innerJoin( + remoteExitNodes, + eq(remoteExitNodes.exitNodeId, exitNodeOrgs.exitNodeId) + ); +} + +export type ListRemoteExitNodesResponse = { + remoteExitNodes: Awaited>; + pagination: { total: number; limit: number; offset: number }; +}; + +export async function listRemoteExitNodes( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedQuery = listRemoteExitNodesSchema.safeParse(req.query); + if (!parsedQuery.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedQuery.error) + ) + ); + } + const { limit, offset } = parsedQuery.data; + + const parsedParams = listRemoteExitNodesParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error) + ) + ); + } + const { orgId } = parsedParams.data; + + if (req.user && orgId && orgId !== req.userOrgId) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + const baseQuery = queryRemoteExitNodes(orgId); + + const countQuery = db + .select({ count: count() }) + .from(remoteExitNodes) + .innerJoin( + exitNodes, + eq(exitNodes.exitNodeId, remoteExitNodes.exitNodeId) + ) + .where(eq(exitNodes.type, "remoteExitNode")); + + const remoteExitNodesList = await baseQuery.limit(limit).offset(offset); + const totalCountResult = await countQuery; + const totalCount = totalCountResult[0].count; + + return response(res, { + data: { + remoteExitNodes: remoteExitNodesList, + pagination: { + total: totalCount, + limit, + offset + } + }, + success: true, + error: false, + message: "Remote exit nodes retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts b/server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts new file mode 100644 index 00000000..684e616c --- /dev/null +++ b/server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts @@ -0,0 +1,71 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { generateId } from "@server/auth/sessions/app"; +import { fromError } from "zod-validation-error"; +import { z } from "zod"; + +export type PickRemoteExitNodeDefaultsResponse = { + remoteExitNodeId: string; + secret: string; +}; + +const paramsSchema = z + .object({ + orgId: z.string() + }) + .strict(); + +export async function pickRemoteExitNodeDefaults( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { orgId } = parsedParams.data; + + const remoteExitNodeId = generateId(15); + const secret = generateId(48); + + return response(res, { + data: { + remoteExitNodeId, + secret + }, + success: true, + error: false, + message: "Organization retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts b/server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts new file mode 100644 index 00000000..689580b9 --- /dev/null +++ b/server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts @@ -0,0 +1,170 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { NextFunction, Request, Response } from "express"; +import { db, exitNodes, exitNodeOrgs } from "@server/db"; +import HttpCode from "@server/types/HttpCode"; +import { remoteExitNodes } from "@server/db"; +import createHttpError from "http-errors"; +import response from "@server/lib/response"; +import { SqliteError } from "better-sqlite3"; +import moment from "moment"; +import { generateId } from "@server/auth/sessions/app"; +import { hashPassword } from "@server/auth/password"; +import logger from "@server/logger"; +import z from "zod"; +import { fromError } from "zod-validation-error"; + +export type QuickStartRemoteExitNodeResponse = { + remoteExitNodeId: string; + secret: string; +}; + +const INSTALLER_KEY = "af4e4785-7e09-11f0-b93a-74563c4e2a7e"; + +const quickStartRemoteExitNodeBodySchema = z.object({ + token: z.string() +}); + +export async function quickStartRemoteExitNode( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedBody = quickStartRemoteExitNodeBodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { token } = parsedBody.data; + + const tokenValidation = validateTokenOnApi(token); + if (!tokenValidation.isValid) { + logger.info(`Failed token validation: ${tokenValidation.message}`); + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + fromError(tokenValidation.message).toString() + ) + ); + } + + const remoteExitNodeId = generateId(15); + const secret = generateId(48); + const secretHash = await hashPassword(secret); + + await db.insert(remoteExitNodes).values({ + remoteExitNodeId, + secretHash, + dateCreated: moment().toISOString() + }); + + return response(res, { + data: { + remoteExitNodeId, + secret + }, + success: true, + error: false, + message: "Remote exit node created successfully", + status: HttpCode.OK + }); + } catch (e) { + if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "A remote exit node with that ID already exists" + ) + ); + } else { + logger.error("Failed to create remoteExitNode", e); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to create remoteExitNode" + ) + ); + } + } +} + +/** + * Validates a token received from the frontend. + * @param {string} token The validation token from the request. + * @returns {{ isValid: boolean; message: string }} An object indicating if the token is valid. + */ +const validateTokenOnApi = ( + token: string +): { isValid: boolean; message: string } => { + if (!token) { + return { isValid: false, message: "Error: No token provided." }; + } + + try { + // 1. Decode the base64 string + const decodedB64 = atob(token); + + // 2. Reverse the character code manipulation + const deobfuscated = decodedB64 + .split("") + .map((char) => String.fromCharCode(char.charCodeAt(0) - 5)) // Reverse the shift + .join(""); + + // 3. Split the data to get the original secret and timestamp + const parts = deobfuscated.split("|"); + if (parts.length !== 2) { + throw new Error("Invalid token format."); + } + const receivedKey = parts[0]; + const tokenTimestamp = parseInt(parts[1], 10); + + // 4. Check if the secret key matches + if (receivedKey !== INSTALLER_KEY) { + logger.info(`Token key mismatch. Received: ${receivedKey}`); + return { isValid: false, message: "Invalid token: Key mismatch." }; + } + + // 5. Check if the timestamp is recent (e.g., within 30 seconds) to prevent replay attacks + const now = Date.now(); + const timeDifference = now - tokenTimestamp; + + if (timeDifference > 30000) { + // 30 seconds + return { isValid: false, message: "Invalid token: Expired." }; + } + + if (timeDifference < 0) { + // Timestamp is in the future + return { + isValid: false, + message: "Invalid token: Timestamp is in the future." + }; + } + + // If all checks pass, the token is valid + return { isValid: true, message: "Token is valid!" }; + } catch (error) { + // This will catch errors from atob (if not valid base64) or other issues. + return { + isValid: false, + message: `Error: ${(error as Error).message}` + }; + } +}; diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 60dfc0cb..53af0b72 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, loginPage } from "@server/db"; import { domains, orgDomains, @@ -21,6 +21,7 @@ import { subdomainSchema } from "@server/lib/schemas"; import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; import { build } from "@server/build"; +import { createCertificate } from "../private/certificates/createCertificate"; import { getUniqueResourceName } from "@server/db/names"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; @@ -54,7 +55,7 @@ const createRawResourceSchema = z name: z.string().min(1).max(255), http: z.boolean(), protocol: z.enum(["tcp", "udp"]), - proxyPort: z.number().int().min(1).max(65535), + proxyPort: z.number().int().min(1).max(65535) // enableProxy: z.boolean().default(true) // always true now }) .strict() @@ -142,10 +143,7 @@ export async function createResource( const { http } = req.body; if (http) { - return await createHttpResource( - { req, res, next }, - { orgId } - ); + return await createHttpResource({ req, res, next }, { orgId }); } else { if ( !config.getRawConfig().flags?.allow_raw_resources && @@ -158,10 +156,7 @@ export async function createResource( ) ); } - return await createRawResource( - { req, res, next }, - { orgId } - ); + return await createRawResource({ req, res, next }, { orgId }); } } catch (error) { logger.error(error); @@ -198,15 +193,14 @@ async function createHttpResource( const subdomain = parsedBody.data.subdomain; // Validate domain and construct full domain - const domainResult = await validateAndConstructDomain(domainId, orgId, subdomain); - + const domainResult = await validateAndConstructDomain( + domainId, + orgId, + subdomain + ); + if (!domainResult.success) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - domainResult.error - ) - ); + return next(createHttpError(HttpCode.BAD_REQUEST, domainResult.error)); } const { fullDomain, subdomain: finalSubdomain } = domainResult; @@ -228,6 +222,22 @@ async function createHttpResource( ); } + if (build != "oss") { + const existingLoginPages = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, fullDomain)); + + if (existingLoginPages.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Login page with that domain already exists" + ) + ); + } + } + let resource: Resource | undefined; const niceId = await getUniqueResourceName(orgId); @@ -285,6 +295,10 @@ async function createHttpResource( ); } + if (build != "oss") { + await createCertificate(domainId, fullDomain, db); + } + return response(res, { data: resource, success: true, @@ -332,7 +346,7 @@ async function createRawResource( name, http, protocol, - proxyPort, + proxyPort // enableProxy }) .returning(); diff --git a/server/routers/resource/createResourceRule.ts b/server/routers/resource/createResourceRule.ts index affd7625..7cb83d8b 100644 --- a/server/routers/resource/createResourceRule.ts +++ b/server/routers/resource/createResourceRule.ts @@ -18,7 +18,7 @@ import { OpenAPITags, registry } from "@server/openApi"; const createResourceRuleSchema = z .object({ action: z.enum(["ACCEPT", "DROP", "PASS"]), - match: z.enum(["CIDR", "IP", "PATH"]), + match: z.enum(["CIDR", "IP", "PATH", "GEOIP"]), value: z.string().min(1), priority: z.number().int(), enabled: z.boolean().optional() diff --git a/server/routers/resource/getExchangeToken.ts b/server/routers/resource/getExchangeToken.ts index ba01f63b..605e5ca6 100644 --- a/server/routers/resource/getExchangeToken.ts +++ b/server/routers/resource/getExchangeToken.ts @@ -14,7 +14,7 @@ import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; const getExchangeTokenParams = z .object({ diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index 006495a7..f6c8c596 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -1,17 +1,14 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { - resourcePassword, - resourcePincode, - resources -} from "@server/db"; +import { resourcePassword, resourcePincode, resources } from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; +import { build } from "@server/build"; const getResourceAuthInfoSchema = z .object({ @@ -52,19 +49,36 @@ export async function getResourceAuthInfo( const { resourceGuid } = parsedParams.data; - const [result] = await db - .select() - .from(resources) - .leftJoin( - resourcePincode, - eq(resourcePincode.resourceId, resources.resourceId) - ) - .leftJoin( - resourcePassword, - eq(resourcePassword.resourceId, resources.resourceId) - ) - .where(eq(resources.resourceGuid, resourceGuid)) - .limit(1); + const isGuidInteger = /^\d+$/.test(resourceGuid); + + const [result] = + isGuidInteger && build === "saas" + ? await db + .select() + .from(resources) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId) + ) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId) + ) + .where(eq(resources.resourceId, Number(resourceGuid))) + .limit(1) + : await db + .select() + .from(resources) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId) + ) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId) + ) + .where(eq(resources.resourceGuid, resourceGuid)) + .limit(1); const resource = result?.resources; const pincode = result?.resourcePincode; diff --git a/server/routers/resource/setResourcePassword.ts b/server/routers/resource/setResourcePassword.ts index d1d4a655..5ff485d2 100644 --- a/server/routers/resource/setResourcePassword.ts +++ b/server/routers/resource/setResourcePassword.ts @@ -7,7 +7,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; import { hash } from "@node-rs/argon2"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; import { OpenAPITags, registry } from "@server/openApi"; diff --git a/server/routers/resource/setResourcePincode.ts b/server/routers/resource/setResourcePincode.ts index d8553c8c..83af3c7a 100644 --- a/server/routers/resource/setResourcePincode.ts +++ b/server/routers/resource/setResourcePincode.ts @@ -7,7 +7,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; import { hash } from "@node-rs/argon2"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import stoi from "@server/lib/stoi"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index bb0a6b55..83fcf6f1 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, loginPage } from "@server/db"; import { domains, Org, @@ -20,8 +20,10 @@ import { tlsNameSchema } from "@server/lib/schemas"; import { subdomainSchema } from "@server/lib/schemas"; import { registry } from "@server/openApi"; import { OpenAPITags } from "@server/openApi"; +import { createCertificate } from "../private/certificates/createCertificate"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { validateHeaders } from "@server/lib/validators"; +import { build } from "@server/build"; const updateResourceParamsSchema = z .object({ @@ -47,7 +49,10 @@ const updateHttpResourceBodySchema = z tlsServerName: z.string().nullable().optional(), setHostHeader: z.string().nullable().optional(), skipToIdpId: z.number().int().positive().nullable().optional(), - headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional(), + headers: z + .array(z.object({ name: z.string(), value: z.string() })) + .nullable() + .optional() }) .strict() .refine((data) => Object.keys(data).length > 0, { @@ -234,14 +239,15 @@ async function updateHttpResource( const domainId = updateData.domainId; // Validate domain and construct full domain - const domainResult = await validateAndConstructDomain(domainId, resource.orgId, updateData.subdomain); - + const domainResult = await validateAndConstructDomain( + domainId, + resource.orgId, + updateData.subdomain + ); + if (!domainResult.success) { return next( - createHttpError( - HttpCode.BAD_REQUEST, - domainResult.error - ) + createHttpError(HttpCode.BAD_REQUEST, domainResult.error) ); } @@ -266,6 +272,22 @@ async function updateHttpResource( ) ); } + + if (build != "oss") { + const existingLoginPages = await db + .select() + .from(loginPage) + .where(eq(loginPage.fullDomain, fullDomain)); + + if (existingLoginPages.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + "Login page with that domain already exists" + ) + ); + } + } } // update the full domain if it has changed @@ -278,6 +300,10 @@ async function updateHttpResource( // Update the subdomain in the update data updateData.subdomain = finalSubdomain; + + if (build != "oss") { + await createCertificate(domainId, fullDomain, db); + } } let headers = null; diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts index c2b6a47a..06061da9 100644 --- a/server/routers/resource/updateResourceRule.ts +++ b/server/routers/resource/updateResourceRule.ts @@ -30,7 +30,7 @@ const updateResourceRuleParamsSchema = z const updateResourceRuleSchema = z .object({ action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(), - match: z.enum(["CIDR", "IP", "PATH"]).optional(), + match: z.enum(["CIDR", "IP", "PATH", "GEOIP"]).optional(), value: z.string().min(1).optional(), priority: z.number().int(), enabled: z.boolean().optional() diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 9d3ab692..5ffa6954 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -42,15 +42,15 @@ const createSiteSchema = z address: z.string().optional(), type: z.enum(["newt", "wireguard", "local"]) }) - .strict() - .refine((data) => { - if (data.type === "local") { - return !config.getRawConfig().flags?.disable_local_sites; - } else if (data.type === "wireguard") { - return !config.getRawConfig().flags?.disable_basic_wireguard_sites; - } - return true; - }); + .strict(); +// .refine((data) => { +// if (data.type === "local") { +// return !config.getRawConfig().flags?.disable_local_sites; +// } else if (data.type === "wireguard") { +// return !config.getRawConfig().flags?.disable_basic_wireguard_sites; +// } +// return true; +// }); export type CreateSiteBody = z.infer; diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index b2655ff6..694556f7 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -1,4 +1,4 @@ -import { db, newts } from "@server/db"; +import { db, exitNodes, newts } from "@server/db"; import { orgs, roleSites, sites, userSites } from "@server/db"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; @@ -105,11 +105,15 @@ function querySites(orgId: string, accessibleSiteIds: number[]) { type: sites.type, online: sites.online, address: sites.address, - newtVersion: newts.version + newtVersion: newts.version, + exitNodeId: sites.exitNodeId, + exitNodeName: exitNodes.name, + exitNodeEndpoint: exitNodes.endpoint }) .from(sites) .leftJoin(orgs, eq(sites.orgId, orgs.orgId)) .leftJoin(newts, eq(newts.siteId, sites.siteId)) + .leftJoin(exitNodes, eq(exitNodes.exitNodeId, sites.exitNodeId)) .where( and( inArray(sites.siteId, accessibleSiteIds), diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 58d44744..46d3c53b 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -74,6 +74,12 @@ export async function pickSiteDefaults( const randomExitNode = exitNodesList[Math.floor(Math.random() * exitNodesList.length)]; + if (!randomExitNode) { + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "No available exit node") + ); + } + // TODO: this probably can be optimized... // list all of the sites on that exit node const sitesQuery = await db @@ -86,6 +92,7 @@ export async function pickSiteDefaults( // TODO: we need to lock this subnet for some time so someone else does not take it const subnets = sitesQuery .map((site) => site.subnet) + .filter((subnet) => subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet)) .filter((subnet) => subnet !== null); // exclude the exit node address by replacing after the / with a site block size subnets.push( diff --git a/server/routers/supporterKey/hideSupporterKey.ts b/server/routers/supporterKey/hideSupporterKey.ts index f9d4e89b..e5441259 100644 --- a/server/routers/supporterKey/hideSupporterKey.ts +++ b/server/routers/supporterKey/hideSupporterKey.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import config from "@server/lib/config"; export type HideSupporterKeyResponse = { diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index 94d0815b..0e958889 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -2,12 +2,13 @@ import { Request, Response, NextFunction } from "express"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import config from "@server/lib/config"; import { db } from "@server/db"; import { count } from "drizzle-orm"; import { users } from "@server/db"; import license from "@server/license/license"; +import { build } from "@server/build"; export type IsSupporterKeyVisibleResponse = { visible: boolean; @@ -44,6 +45,10 @@ export async function isSupporterKeyVisible( } } + if (config.getRawPrivateConfig().flags?.hide_supporter_key && build != "oss") { + visible = false; + } + return sendResponse(res, { data: { visible, diff --git a/server/routers/supporterKey/validateSupporterKey.ts b/server/routers/supporterKey/validateSupporterKey.ts index a365030a..9d949fb5 100644 --- a/server/routers/supporterKey/validateSupporterKey.ts +++ b/server/routers/supporterKey/validateSupporterKey.ts @@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { response as sendResponse } from "@server/lib"; +import { response as sendResponse } from "@server/lib/response"; import { suppressDeprecationWarnings } from "moment"; import { supporterKey } from "@server/db"; import { db } from "@server/db"; diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index dd85c888..0b473563 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, TargetHealthCheck, targetHealthCheck } from "@server/db"; import { newts, resources, sites, Target, targets } from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -31,6 +31,25 @@ const createTargetSchema = z method: z.string().optional().nullable(), port: z.number().int().min(1).max(65535), enabled: z.boolean().default(true), + hcEnabled: z.boolean().optional(), + hcPath: z.string().min(1).optional().nullable(), + hcScheme: z.string().optional().nullable(), + hcMode: z.string().optional().nullable(), + hcHostname: z.string().optional().nullable(), + hcPort: z.number().int().positive().optional().nullable(), + hcInterval: z.number().int().positive().min(5).optional().nullable(), + hcUnhealthyInterval: z + .number() + .int() + .positive() + .min(5) + .optional() + .nullable(), + hcTimeout: z.number().int().positive().min(1).optional().nullable(), + hcHeaders: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional(), + hcFollowRedirects: z.boolean().optional().nullable(), + hcMethod: z.string().min(1).optional().nullable(), + hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), @@ -38,7 +57,7 @@ const createTargetSchema = z }) .strict(); -export type CreateTargetResponse = Target; +export type CreateTargetResponse = Target & TargetHealthCheck; registry.registerPath({ method: "put", @@ -143,6 +162,7 @@ export async function createTarget( } let newTarget: Target[] = []; + let healthCheck: TargetHealthCheck[] = []; if (site.type == "local") { newTarget = await db .insert(targets) @@ -165,7 +185,10 @@ export async function createTarget( ); } - const { internalPort, targetIps } = await pickPort(site.siteId!, db); + const { internalPort, targetIps } = await pickPort( + site.siteId!, + db + ); if (!internalPort) { return next( @@ -180,8 +203,40 @@ export async function createTarget( .insert(targets) .values({ resourceId, + siteId: site.siteId, + ip: targetData.ip, + method: targetData.method, + port: targetData.port, internalPort, - ...targetData + enabled: targetData.enabled, + path: targetData.path, + pathMatchType: targetData.pathMatchType + }) + .returning(); + + let hcHeaders = null; + if (targetData.hcHeaders) { + hcHeaders = JSON.stringify(targetData.hcHeaders); + } + + healthCheck = await db + .insert(targetHealthCheck) + .values({ + targetId: newTarget[0].targetId, + hcEnabled: targetData.hcEnabled ?? false, + hcPath: targetData.hcPath ?? null, + hcScheme: targetData.hcScheme ?? null, + hcMode: targetData.hcMode ?? null, + hcHostname: targetData.hcHostname ?? null, + hcPort: targetData.hcPort ?? null, + hcInterval: targetData.hcInterval ?? null, + hcUnhealthyInterval: targetData.hcUnhealthyInterval ?? null, + hcTimeout: targetData.hcTimeout ?? null, + hcHeaders: hcHeaders, + hcFollowRedirects: targetData.hcFollowRedirects ?? null, + hcMethod: targetData.hcMethod ?? null, + hcStatus: targetData.hcStatus ?? null, + hcHealth: "unknown" }) .returning(); @@ -205,6 +260,7 @@ export async function createTarget( await addTargets( newt.newtId, newTarget, + healthCheck, resource.protocol, resource.proxyPort ); @@ -213,7 +269,10 @@ export async function createTarget( } return response(res, { - data: newTarget[0], + data: { + ...newTarget[0], + ...healthCheck[0] + }, success: true, error: false, message: "Target created successfully", diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index b0691087..864c02eb 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, Target } from "@server/db"; +import { db, Target, targetHealthCheck, TargetHealthCheck } from "@server/db"; import { targets } from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; @@ -16,7 +16,9 @@ const getTargetSchema = z }) .strict(); -type GetTargetResponse = Target; +type GetTargetResponse = Target & Omit & { + hcHeaders: { name: string; value: string; }[] | null; +}; registry.registerPath({ method: "get", @@ -62,8 +64,29 @@ export async function getTarget( ); } + const [targetHc] = await db + .select() + .from(targetHealthCheck) + .where(eq(targetHealthCheck.targetId, targetId)) + .limit(1); + + // Parse hcHeaders from JSON string back to array + let parsedHcHeaders = null; + if (targetHc?.hcHeaders) { + try { + parsedHcHeaders = JSON.parse(targetHc.hcHeaders); + } catch (error) { + // If parsing fails, keep as string for backward compatibility + parsedHcHeaders = targetHc.hcHeaders; + } + } + return response(res, { - data: target[0], + data: { + ...target[0], + ...targetHc, + hcHeaders: parsedHcHeaders + }, success: true, error: false, message: "Target retrieved successfully", diff --git a/server/routers/target/handleHealthcheckStatusMessage.ts b/server/routers/target/handleHealthcheckStatusMessage.ts new file mode 100644 index 00000000..d726b5af --- /dev/null +++ b/server/routers/target/handleHealthcheckStatusMessage.ts @@ -0,0 +1,114 @@ +import { db, targets, resources, sites, targetHealthCheck } from "@server/db"; +import { MessageHandler } from "../ws"; +import { Newt } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import logger from "@server/logger"; +import { unknown } from "zod"; + +interface TargetHealthStatus { + status: string; + lastCheck: string; + checkCount: number; + lastError?: string; + config: { + id: string; + hcEnabled: boolean; + hcPath?: string; + hcScheme?: string; + hcMode?: string; + hcHostname?: string; + hcPort?: number; + hcInterval?: number; + hcUnhealthyInterval?: number; + hcTimeout?: number; + hcHeaders?: any; + hcMethod?: string; + }; +} + +interface HealthcheckStatusMessage { + targets: Record; +} + +export const handleHealthcheckStatusMessage: MessageHandler = async (context) => { + const { message, client: c } = context; + const newt = c as Newt; + + logger.info("Handling healthcheck status message"); + + if (!newt) { + logger.warn("Newt not found"); + return; + } + + if (!newt.siteId) { + logger.warn("Newt has no site ID"); + return; + } + + const data = message.data as HealthcheckStatusMessage; + + if (!data.targets) { + logger.warn("No targets data in healthcheck status message"); + return; + } + + try { + let successCount = 0; + let errorCount = 0; + + // Process each target status update + for (const [targetId, healthStatus] of Object.entries(data.targets)) { + logger.debug(`Processing health status for target ${targetId}: ${healthStatus.status}${healthStatus.lastError ? ` (${healthStatus.lastError})` : ''}`); + + // Verify the target belongs to this newt's site before updating + // This prevents unauthorized updates to targets from other sites + const targetIdNum = parseInt(targetId); + if (isNaN(targetIdNum)) { + logger.warn(`Invalid target ID: ${targetId}`); + errorCount++; + continue; + } + + const [targetCheck] = await db + .select({ + targetId: targets.targetId, + siteId: targets.siteId + }) + .from(targets) + .innerJoin(resources, eq(targets.resourceId, resources.resourceId)) + .innerJoin(sites, eq(targets.siteId, sites.siteId)) + .where( + and( + eq(targets.targetId, targetIdNum), + eq(sites.siteId, newt.siteId) + ) + ) + .limit(1); + + if (!targetCheck) { + logger.warn(`Target ${targetId} not found or does not belong to site ${newt.siteId}`); + errorCount++; + continue; + } + + // Update the target's health status in the database + await db + .update(targetHealthCheck) + .set({ + hcHealth: healthStatus.status + }) + .where(eq(targetHealthCheck.targetId, targetIdNum)) + .execute(); + + logger.debug(`Updated health status for target ${targetId} to ${healthStatus.status}`); + successCount++; + } + + logger.debug(`Health status update complete: ${successCount} successful, ${errorCount} errors out of ${Object.keys(data.targets).length} targets`); + } catch (error) { + logger.error("Error processing healthcheck status message:", error); + } + + return; +}; diff --git a/server/routers/target/index.ts b/server/routers/target/index.ts index dc1323f7..7d023bbd 100644 --- a/server/routers/target/index.ts +++ b/server/routers/target/index.ts @@ -3,3 +3,4 @@ export * from "./createTarget"; export * from "./deleteTarget"; export * from "./updateTarget"; export * from "./listTargets"; +export * from "./handleHealthcheckStatusMessage"; diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 4a1d99a0..178ec967 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -1,4 +1,4 @@ -import { db, sites } from "@server/db"; +import { db, sites, targetHealthCheck } from "@server/db"; import { targets } from "@server/db"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; @@ -45,6 +45,20 @@ function queryTargets(resourceId: number) { resourceId: targets.resourceId, siteId: targets.siteId, siteType: sites.type, + hcEnabled: targetHealthCheck.hcEnabled, + hcPath: targetHealthCheck.hcPath, + hcScheme: targetHealthCheck.hcScheme, + hcMode: targetHealthCheck.hcMode, + hcHostname: targetHealthCheck.hcHostname, + hcPort: targetHealthCheck.hcPort, + hcInterval: targetHealthCheck.hcInterval, + hcUnhealthyInterval: targetHealthCheck.hcUnhealthyInterval, + hcTimeout: targetHealthCheck.hcTimeout, + hcHeaders: targetHealthCheck.hcHeaders, + hcFollowRedirects: targetHealthCheck.hcFollowRedirects, + hcMethod: targetHealthCheck.hcMethod, + hcStatus: targetHealthCheck.hcStatus, + hcHealth: targetHealthCheck.hcHealth, path: targets.path, pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, @@ -52,13 +66,21 @@ function queryTargets(resourceId: number) { }) .from(targets) .leftJoin(sites, eq(sites.siteId, targets.siteId)) + .leftJoin( + targetHealthCheck, + eq(targetHealthCheck.targetId, targets.targetId) + ) .where(eq(targets.resourceId, resourceId)); return baseQuery; } +type TargetWithParsedHeaders = Omit>[0], 'hcHeaders'> & { + hcHeaders: { name: string; value: string; }[] | null; +}; + export type ListTargetsResponse = { - targets: Awaited>; + targets: TargetWithParsedHeaders[]; pagination: { total: number; limit: number; offset: number }; }; @@ -113,9 +135,26 @@ export async function listTargets( const totalCountResult = await countQuery; const totalCount = totalCountResult[0].count; + // Parse hcHeaders from JSON string back to array for each target + const parsedTargetsList = targetsList.map(target => { + let parsedHcHeaders = null; + if (target.hcHeaders) { + try { + parsedHcHeaders = JSON.parse(target.hcHeaders); + } catch (error) { + // If parsing fails, keep as string for backward compatibility + parsedHcHeaders = target.hcHeaders; + } + } + return { + ...target, + hcHeaders: parsedHcHeaders + }; + }); + return response(res, { data: { - targets: targetsList, + targets: parsedTargetsList, pagination: { total: totalCount, limit, diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 1e47ce96..af629729 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, targetHealthCheck } from "@server/db"; import { newts, resources, sites, targets } from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; @@ -13,6 +13,7 @@ import { addTargets } from "../newt/targets"; import { pickPort } from "./helpers"; import { isTargetValid } from "@server/lib/validators"; import { OpenAPITags, registry } from "@server/openApi"; +import { vs } from "@react-email/components"; const updateTargetParamsSchema = z .object({ @@ -27,6 +28,25 @@ const updateTargetBodySchema = z method: z.string().min(1).max(10).optional().nullable(), port: z.number().int().min(1).max(65535).optional(), enabled: z.boolean().optional(), + hcEnabled: z.boolean().optional().nullable(), + hcPath: z.string().min(1).optional().nullable(), + hcScheme: z.string().optional().nullable(), + hcMode: z.string().optional().nullable(), + hcHostname: z.string().optional().nullable(), + hcPort: z.number().int().positive().optional().nullable(), + hcInterval: z.number().int().positive().min(5).optional().nullable(), + hcUnhealthyInterval: z + .number() + .int() + .positive() + .min(5) + .optional() + .nullable(), + hcTimeout: z.number().int().positive().min(1).optional().nullable(), + hcHeaders: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional(), + hcFollowRedirects: z.boolean().optional().nullable(), + hcMethod: z.string().min(1).optional().nullable(), + hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), @@ -171,12 +191,43 @@ export async function updateTarget( const [updatedTarget] = await db .update(targets) .set({ - ...parsedBody.data, - internalPort + siteId: parsedBody.data.siteId, + ip: parsedBody.data.ip, + method: parsedBody.data.method, + port: parsedBody.data.port, + internalPort, + enabled: parsedBody.data.enabled, + path: parsedBody.data.path, + pathMatchType: parsedBody.data.pathMatchType }) .where(eq(targets.targetId, targetId)) .returning(); + let hcHeaders = null; + if (parsedBody.data.hcHeaders) { + hcHeaders = JSON.stringify(parsedBody.data.hcHeaders); + } + + const [updatedHc] = await db + .update(targetHealthCheck) + .set({ + hcEnabled: parsedBody.data.hcEnabled || false, + hcPath: parsedBody.data.hcPath, + hcScheme: parsedBody.data.hcScheme, + hcMode: parsedBody.data.hcMode, + hcHostname: parsedBody.data.hcHostname, + hcPort: parsedBody.data.hcPort, + hcInterval: parsedBody.data.hcInterval, + hcUnhealthyInterval: parsedBody.data.hcUnhealthyInterval, + hcTimeout: parsedBody.data.hcTimeout, + hcHeaders: hcHeaders, + hcFollowRedirects: parsedBody.data.hcFollowRedirects, + hcMethod: parsedBody.data.hcMethod, + hcStatus: parsedBody.data.hcStatus + }) + .where(eq(targetHealthCheck.targetId, targetId)) + .returning(); + if (site.pubKey) { if (site.type == "wireguard") { await addPeer(site.exitNodeId!, { @@ -194,13 +245,17 @@ export async function updateTarget( await addTargets( newt.newtId, [updatedTarget], + [updatedHc], resource.protocol, resource.proxyPort ); } } return response(res, { - data: updatedTarget, + data: { + ...updatedTarget, + ...updatedHc + }, success: true, error: false, message: "Target updated successfully", diff --git a/server/routers/traefik/index.ts b/server/routers/traefik/index.ts index 5630028c..6f5bd4f0 100644 --- a/server/routers/traefik/index.ts +++ b/server/routers/traefik/index.ts @@ -1 +1 @@ -export * from "./getTraefikConfig"; \ No newline at end of file +export * from "./traefikConfigProvider"; \ No newline at end of file diff --git a/server/routers/traefik/traefikConfigProvider.ts b/server/routers/traefik/traefikConfigProvider.ts new file mode 100644 index 00000000..50ec0770 --- /dev/null +++ b/server/routers/traefik/traefikConfigProvider.ts @@ -0,0 +1,61 @@ +import { Request, Response } from "express"; +import logger from "@server/logger"; +import HttpCode from "@server/types/HttpCode"; +import config from "@server/lib/config"; +import { build } from "@server/build"; +import { getTraefikConfig } from "@server/lib/traefik"; +import { getCurrentExitNodeId } from "@server/lib/exitNodes"; + +const badgerMiddlewareName = "badger"; + +export async function traefikConfigProvider( + _: Request, + res: Response +): Promise { + try { + // First query to get resources with site and org info + // Get the current exit node name from config + const currentExitNodeId = await getCurrentExitNodeId(); + + const traefikConfig = await getTraefikConfig( + currentExitNodeId, + config.getRawConfig().traefik.site_types, + build == "oss", // filter out the namespace domains in open source + build != "oss" // generate the login pages on the cloud and hybrid + ); + + if (traefikConfig?.http?.middlewares) { + // BECAUSE SOMETIMES THE CONFIG CAN BE EMPTY IF THERE IS NOTHING + traefikConfig.http.middlewares[badgerMiddlewareName] = { + plugin: { + [badgerMiddlewareName]: { + apiBaseUrl: new URL( + "/api/v1", + `http://${ + config.getRawConfig().server.internal_hostname + }:${config.getRawConfig().server.internal_port}` + ).href, + userSessionCookieName: + config.getRawConfig().server.session_cookie_name, + + // deprecated + accessTokenQueryParam: + config.getRawConfig().server + .resource_access_token_param, + + resourceSessionRequestParam: + config.getRawConfig().server + .resource_session_request_param + } + } + }; + } + + return res.status(HttpCode.OK).json(traefikConfig); + } catch (e) { + logger.error(`Failed to build Traefik config: ${e}`); + return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({ + error: "Failed to build Traefik config" + }); + } +} \ No newline at end of file diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index 73bed018..b553bd19 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -10,6 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { verifySession } from "@server/auth/sessions/verifySession"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; const acceptInviteBodySchema = z .object({ @@ -131,6 +133,14 @@ export async function acceptInvite( .where(eq(userOrgs.orgId, existingInvite.orgId)); }); + if (totalUsers) { + await usageService.updateDaily( + existingInvite.orgId, + FeatureId.USERS, + totalUsers.length + ); + } + return response(res, { data: { accepted: true, orgId: existingInvite.orgId }, success: true, diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index 5b11c923..b8f681c3 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -10,6 +10,11 @@ import { db, UserOrg } from "@server/db"; import { and, eq } from "drizzle-orm"; import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db"; import { generateId } from "@server/auth/sessions/app"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { build } from "@server/build"; +import { getOrgTierData } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; const paramsSchema = z .object({ @@ -93,6 +98,35 @@ export async function createOrgUser( roleId } = parsedBody.data; + if (build == "saas") { + const usage = await usageService.getUsage(orgId, FeatureId.USERS); + if (!usage) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "No usage data found for this organization" + ) + ); + } + const rejectUsers = await usageService.checkLimitSet( + orgId, + false, + FeatureId.USERS, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectUsers) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User limit exceeded. Please upgrade your plan." + ) + ); + } + } + const [role] = await db .select() .from(roles) @@ -112,6 +146,19 @@ export async function createOrgUser( ) ); } else if (type === "oidc") { + if (build === "saas") { + const { tier } = await getOrgTierData(orgId); + const subscribed = tier === TierId.STANDARD; + if (!subscribed) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "This organization's current plan does not support this feature." + ) + ); + } + } + if (!idpId) { return next( createHttpError( @@ -218,6 +265,14 @@ export async function createOrgUser( .from(userOrgs) .where(eq(userOrgs.orgId, orgId)); }); + + if (orgUsers) { + await usageService.updateDaily( + orgId, + FeatureId.USERS, + orgUsers.length + ); + } } else { return next( createHttpError(HttpCode.BAD_REQUEST, "User type is required") diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 174600fc..746a383b 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -17,6 +17,9 @@ import { sendEmail } from "@server/emails"; import SendInviteLink from "@server/emails/templates/SendInviteLink"; import { OpenAPITags, registry } from "@server/openApi"; import { UserType } from "@server/types/UserTypes"; +import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { build } from "@server/build"; const regenerateTracker = new NodeCache({ stdTTL: 3600, checkperiod: 600 }); @@ -28,10 +31,7 @@ const inviteUserParamsSchema = z const inviteUserBodySchema = z .object({ - email: z - .string() - .toLowerCase() - .email(), + email: z.string().toLowerCase().email(), roleId: z.number(), validHours: z.number().gt(0).lte(168), sendEmail: z.boolean().optional(), @@ -99,7 +99,6 @@ export async function inviteUser( regenerate } = parsedBody.data; - // Check if the organization exists const org = await db .select() @@ -112,6 +111,35 @@ export async function inviteUser( ); } + if (build == "saas") { + const usage = await usageService.getUsage(orgId, FeatureId.USERS); + if (!usage) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "No usage data found for this organization" + ) + ); + } + const rejectUsers = await usageService.checkLimitSet( + orgId, + false, + FeatureId.USERS, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectUsers) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User limit exceeded. Please upgrade your plan." + ) + ); + } + } + // Check if the user already exists in the `users` table const existingUser = await db .select() diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index dcd8c6f2..6d0c5359 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -2,13 +2,17 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db, resources, sites, UserOrg } from "@server/db"; import { userOrgs, userResources, users, userSites } from "@server/db"; -import { and, eq, exists } from "drizzle-orm"; +import { and, count, eq, exists } from "drizzle-orm"; 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 { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/private/billing"; +import { build } from "@server/build"; +import { UserType } from "@server/types/UserTypes"; const removeUserSchema = z .object({ @@ -115,8 +119,33 @@ export async function removeUserOrg( .select() .from(userOrgs) .where(eq(userOrgs.orgId, orgId)); + + if (build === "saas") { + const [rootUser] = await trx + .select() + .from(users) + .where(eq(users.userId, userId)); + + const [leftInOrgs] = await trx + .select({ count: count() }) + .from(userOrgs) + .where(eq(userOrgs.userId, userId)); + + // if the user is not an internal user and does not belong to any org, delete the entire user + if (rootUser?.type !== UserType.Internal && !leftInOrgs.count) { + await trx.delete(users).where(eq(users.userId, userId)); + } + } }); + if (userCount) { + await usageService.updateDaily( + orgId, + FeatureId.USERS, + userCount.length + ); + } + return response(res, { data: null, success: true, diff --git a/server/routers/ws/index.ts b/server/routers/ws/index.ts index cf95932c..376a960b 100644 --- a/server/routers/ws/index.ts +++ b/server/routers/ws/index.ts @@ -1 +1,24 @@ -export * from "./ws"; \ No newline at end of file +import { build } from "@server/build"; + +// Import both modules +import * as wsModule from "./ws"; +import * as privateWsModule from "./privateWs"; + +// Conditionally export WebSocket implementation based on build type +const wsImplementation = build === "oss" ? wsModule : privateWsModule; + +// Re-export all items from the selected implementation +export const { + router, + handleWSUpgrade, + sendToClient, + broadcastToAllExcept, + connectedClients, + hasActiveConnections, + getActiveNodes, + NODE_ID, + cleanup +} = wsImplementation; + +// Re-export the MessageHandler type (both modules have the same type signature) +export type { MessageHandler } from "./privateWs"; \ No newline at end of file diff --git a/server/routers/ws/messageHandlers.ts b/server/routers/ws/messageHandlers.ts index 8ca33b8a..5b111eec 100644 --- a/server/routers/ws/messageHandlers.ts +++ b/server/routers/ws/messageHandlers.ts @@ -13,7 +13,10 @@ import { handleOlmPingMessage, startOlmOfflineChecker } from "../olm"; -import { MessageHandler } from "./ws"; +import { handleRemoteExitNodeRegisterMessage, handleRemoteExitNodePingMessage, startRemoteExitNodeOfflineChecker } from "@server/routers/private/remoteExitNode"; +import { MessageHandler } from "./privateWs"; +import { handleHealthcheckStatusMessage } from "../target"; +import { build } from "@server/build"; export const messageHandlers: Record = { "newt/wg/register": handleNewtRegisterMessage, @@ -26,6 +29,13 @@ export const messageHandlers: Record = { "newt/socket/containers": handleDockerContainersMessage, "newt/ping/request": handleNewtPingRequestMessage, "newt/blueprint/apply": handleApplyBlueprintMessage, + "newt/healthcheck/status": handleHealthcheckStatusMessage, + + "remoteExitNode/register": handleRemoteExitNodeRegisterMessage, + "remoteExitNode/ping": handleRemoteExitNodePingMessage, }; startOlmOfflineChecker(); // this is to handle the offline check for olms +if (build != "oss") { + startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes +} \ No newline at end of file diff --git a/server/routers/ws/privateWs.ts b/server/routers/ws/privateWs.ts new file mode 100644 index 00000000..52bb94d8 --- /dev/null +++ b/server/routers/ws/privateWs.ts @@ -0,0 +1,892 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Router, Request, Response } from "express"; +import { Server as HttpServer } from "http"; +import { WebSocket, WebSocketServer } from "ws"; +import { IncomingMessage } from "http"; +import { Socket } from "net"; +import { + Newt, + newts, + NewtSession, + olms, + Olm, + OlmSession, + RemoteExitNode, + RemoteExitNodeSession, + remoteExitNodes +} from "@server/db"; +import { eq } from "drizzle-orm"; +import { db } from "@server/db"; +import { validateNewtSessionToken } from "@server/auth/sessions/newt"; +import { validateOlmSessionToken } from "@server/auth/sessions/olm"; +import { messageHandlers } from "./messageHandlers"; +import logger from "@server/logger"; +import redisManager from "@server/db/private/redis"; +import { v4 as uuidv4 } from "uuid"; +import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; +import { rateLimitService } from "@server/db/private/rateLimit"; + +const MAX_PENDING_MESSAGES = 50; // Maximum messages to queue during connection setup + +// Custom interfaces +interface WebSocketRequest extends IncomingMessage { + token?: string; +} + +type ClientType = "newt" | "olm" | "remoteExitNode"; + +interface AuthenticatedWebSocket extends WebSocket { + client?: Newt | Olm | RemoteExitNode; + clientType?: ClientType; + connectionId?: string; + isFullyConnected?: boolean; + pendingMessages?: Buffer[]; +} + +interface TokenPayload { + client: Newt | Olm | RemoteExitNode; + session: NewtSession | OlmSession | RemoteExitNodeSession; + clientType: ClientType; +} + +interface WSMessage { + type: string; + data: any; +} + +interface HandlerResponse { + message: WSMessage; + broadcast?: boolean; + excludeSender?: boolean; + targetClientId?: string; +} + +interface HandlerContext { + message: WSMessage; + senderWs: WebSocket; + client: Newt | Olm | RemoteExitNode | undefined; + clientType: ClientType; + sendToClient: (clientId: string, message: WSMessage) => Promise; + broadcastToAllExcept: ( + message: WSMessage, + excludeClientId?: string + ) => Promise; + connectedClients: Map; +} + +interface RedisMessage { + type: "direct" | "broadcast"; + targetClientId?: string; + excludeClientId?: string; + message: WSMessage; + fromNodeId: string; +} + +export type MessageHandler = ( + context: HandlerContext +) => Promise; + +// Helper function to process a single message +const processMessage = async ( + ws: AuthenticatedWebSocket, + data: Buffer, + clientId: string, + clientType: ClientType +): Promise => { + try { + const message: WSMessage = JSON.parse(data.toString()); + + logger.debug( + `Processing message from ${clientType.toUpperCase()} ID: ${clientId}, type: ${message.type}` + ); + + if (!message.type || typeof message.type !== "string") { + throw new Error("Invalid message format: missing or invalid type"); + } + + // Check rate limiting with message type awareness + const rateLimitResult = await rateLimitService.checkRateLimit( + clientId, + message.type, // Pass message type for granular limiting + 100, // max requests per window + 20, // max requests per message type per window + 60 * 1000 // window in milliseconds + ); + if (rateLimitResult.isLimited) { + const reason = + rateLimitResult.reason === "global" + ? "too many messages" + : `too many '${message.type}' messages`; + logger.debug( + `Rate limit exceeded for ${clientType.toUpperCase()} ID: ${clientId} - ${reason}, ignoring message` + ); + + // Send rate limit error to client + // ws.send(JSON.stringify({ + // type: "rate_limit_error", + // data: { + // message: `Rate limit exceeded: ${reason}`, + // messageType: message.type, + // reason: rateLimitResult.reason + // } + // })); + return; + } + + const handler = messageHandlers[message.type]; + if (!handler) { + throw new Error(`Unsupported message type: ${message.type}`); + } + + const response = await handler({ + message, + senderWs: ws, + client: ws.client, + clientType: ws.clientType!, + sendToClient, + broadcastToAllExcept, + connectedClients + }); + + if (response) { + if (response.broadcast) { + await broadcastToAllExcept( + response.message, + response.excludeSender ? clientId : undefined + ); + } else if (response.targetClientId) { + await sendToClient(response.targetClientId, response.message); + } else { + ws.send(JSON.stringify(response.message)); + } + } + } catch (error) { + logger.error("Message handling error:", error); + // ws.send(JSON.stringify({ + // type: "error", + // data: { + // message: error instanceof Error ? error.message : "Unknown error occurred", + // originalMessage: data.toString() + // } + // })); + } +}; + +// Helper function to process pending messages +const processPendingMessages = async ( + ws: AuthenticatedWebSocket, + clientId: string, + clientType: ClientType +): Promise => { + if (!ws.pendingMessages || ws.pendingMessages.length === 0) { + return; + } + + logger.info( + `Processing ${ws.pendingMessages.length} pending messages for ${clientType.toUpperCase()} ID: ${clientId}` + ); + + let jobs = []; + for (const messageData of ws.pendingMessages) { + jobs.push(processMessage(ws, messageData, clientId, clientType)); + } + + await Promise.all(jobs); + + ws.pendingMessages = []; // Clear pending messages to prevent reprocessing +}; + +const router: Router = Router(); +const wss: WebSocketServer = new WebSocketServer({ noServer: true }); + +// Generate unique node ID for this instance +const NODE_ID = uuidv4(); +const REDIS_CHANNEL = "websocket_messages"; + +// Client tracking map (local to this node) +let connectedClients: Map = new Map(); + +// Recovery tracking +let isRedisRecoveryInProgress = false; + +// Helper to get map key +const getClientMapKey = (clientId: string) => clientId; + +// Redis keys (generalized) +const getConnectionsKey = (clientId: string) => `ws:connections:${clientId}`; +const getNodeConnectionsKey = (nodeId: string, clientId: string) => + `ws:node:${nodeId}:${clientId}`; + +// Initialize Redis subscription for cross-node messaging +const initializeRedisSubscription = async (): Promise => { + if (!redisManager.isRedisEnabled()) return; + + await redisManager.subscribe( + REDIS_CHANNEL, + async (channel: string, message: string) => { + try { + const redisMessage: RedisMessage = JSON.parse(message); + + // Ignore messages from this node + if (redisMessage.fromNodeId === NODE_ID) return; + + if ( + redisMessage.type === "direct" && + redisMessage.targetClientId + ) { + // Send to specific client on this node + await sendToClientLocal( + redisMessage.targetClientId, + redisMessage.message + ); + } else if (redisMessage.type === "broadcast") { + // Broadcast to all clients on this node except excluded + await broadcastToAllExceptLocal( + redisMessage.message, + redisMessage.excludeClientId + ); + } + } catch (error) { + logger.error("Error processing Redis message:", error); + } + } + ); +}; + +// Simple self-healing recovery function +// Each node is responsible for restoring its own connection state to Redis +// This approach is more efficient than cross-node coordination because: +// 1. Each node knows its own connections (source of truth) +// 2. No network overhead from broadcasting state between nodes +// 3. No race conditions from simultaneous updates +// 4. Redis becomes eventually consistent as each node restores independently +// 5. Simpler logic with better fault tolerance +const recoverConnectionState = async (): Promise => { + if (isRedisRecoveryInProgress) { + logger.debug("Redis recovery already in progress, skipping"); + return; + } + + isRedisRecoveryInProgress = true; + logger.info("Starting Redis connection state recovery..."); + + try { + // Each node simply restores its own local connections to Redis + // This is the source of truth - no need for cross-node coordination + await restoreLocalConnectionsToRedis(); + + logger.info("Redis connection state recovery completed - restored local state"); + } catch (error) { + logger.error("Error during Redis recovery:", error); + } finally { + isRedisRecoveryInProgress = false; + } +}; + +const restoreLocalConnectionsToRedis = async (): Promise => { + if (!redisManager.isRedisEnabled()) return; + + logger.info("Restoring local connections to Redis..."); + let restoredCount = 0; + + try { + // Restore all current local connections to Redis + for (const [clientId, clients] of connectedClients.entries()) { + const validClients = clients.filter(client => client.readyState === WebSocket.OPEN); + + if (validClients.length > 0) { + // Add this node to the client's connection list + await redisManager.sadd(getConnectionsKey(clientId), NODE_ID); + + // Store individual connection details + for (const client of validClients) { + if (client.connectionId) { + await redisManager.hset( + getNodeConnectionsKey(NODE_ID, clientId), + client.connectionId, + Date.now().toString() + ); + } + } + restoredCount++; + } + } + + logger.info(`Restored ${restoredCount} client connections to Redis`); + } catch (error) { + logger.error("Failed to restore local connections to Redis:", error); + } +}; + +// Helper functions for client management +const addClient = async ( + clientType: ClientType, + clientId: string, + ws: AuthenticatedWebSocket +): Promise => { + // Generate unique connection ID + const connectionId = uuidv4(); + ws.connectionId = connectionId; + + // Add to local tracking + const mapKey = getClientMapKey(clientId); + const existingClients = connectedClients.get(mapKey) || []; + existingClients.push(ws); + connectedClients.set(mapKey, existingClients); + + // Add to Redis tracking if enabled + if (redisManager.isRedisEnabled()) { + try { + await redisManager.sadd(getConnectionsKey(clientId), NODE_ID); + await redisManager.hset( + getNodeConnectionsKey(NODE_ID, clientId), + connectionId, + Date.now().toString() + ); + } catch (error) { + logger.error("Failed to add client to Redis tracking (connection still functional locally):", error); + } + } + + logger.info( + `Client added to tracking - ${clientType.toUpperCase()} ID: ${clientId}, Connection ID: ${connectionId}, Total connections: ${existingClients.length}` + ); +}; + +const removeClient = async ( + clientType: ClientType, + clientId: string, + ws: AuthenticatedWebSocket +): Promise => { + const mapKey = getClientMapKey(clientId); + const existingClients = connectedClients.get(mapKey) || []; + const updatedClients = existingClients.filter((client) => client !== ws); + if (updatedClients.length === 0) { + connectedClients.delete(mapKey); + + if (redisManager.isRedisEnabled()) { + try { + await redisManager.srem(getConnectionsKey(clientId), NODE_ID); + await redisManager.del(getNodeConnectionsKey(NODE_ID, clientId)); + } catch (error) { + logger.error("Failed to remove client from Redis tracking (cleanup will occur on recovery):", error); + } + } + + logger.info( + `All connections removed for ${clientType.toUpperCase()} ID: ${clientId}` + ); + } else { + connectedClients.set(mapKey, updatedClients); + + if (redisManager.isRedisEnabled() && ws.connectionId) { + try { + await redisManager.hdel( + getNodeConnectionsKey(NODE_ID, clientId), + ws.connectionId + ); + } catch (error) { + logger.error("Failed to remove specific connection from Redis tracking:", error); + } + } + + logger.info( + `Connection removed - ${clientType.toUpperCase()} ID: ${clientId}, Remaining connections: ${updatedClients.length}` + ); + } +}; + +// Local message sending (within this node) +const sendToClientLocal = async ( + clientId: string, + message: WSMessage +): Promise => { + const mapKey = getClientMapKey(clientId); + const clients = connectedClients.get(mapKey); + if (!clients || clients.length === 0) { + return false; + } + const messageString = JSON.stringify(message); + clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.send(messageString); + } + }); + return true; +}; + +const broadcastToAllExceptLocal = async ( + message: WSMessage, + excludeClientId?: string +): Promise => { + connectedClients.forEach((clients, mapKey) => { + const [type, id] = mapKey.split(":"); + if (!(excludeClientId && id === excludeClientId)) { + clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify(message)); + } + }); + } + }); +}; + +// Cross-node message sending (via Redis) +const sendToClient = async ( + clientId: string, + message: WSMessage +): Promise => { + // Try to send locally first + const localSent = await sendToClientLocal(clientId, message); + + // Only send via Redis if the client is not connected locally and Redis is enabled + if (!localSent && redisManager.isRedisEnabled()) { + try { + const redisMessage: RedisMessage = { + type: "direct", + targetClientId: clientId, + message, + fromNodeId: NODE_ID + }; + + await redisManager.publish(REDIS_CHANNEL, JSON.stringify(redisMessage)); + } catch (error) { + logger.error("Failed to send message via Redis, message may be lost:", error); + // Continue execution - local delivery already attempted + } + } else if (!localSent && !redisManager.isRedisEnabled()) { + // Redis is disabled or unavailable - log that we couldn't deliver to remote nodes + logger.debug(`Could not deliver message to ${clientId} - not connected locally and Redis unavailable`); + } + + return localSent; +}; + +const broadcastToAllExcept = async ( + message: WSMessage, + excludeClientId?: string +): Promise => { + // Broadcast locally + await broadcastToAllExceptLocal(message, excludeClientId); + + // If Redis is enabled, also broadcast via Redis pub/sub to other nodes + if (redisManager.isRedisEnabled()) { + try { + const redisMessage: RedisMessage = { + type: "broadcast", + excludeClientId, + message, + fromNodeId: NODE_ID + }; + + await redisManager.publish(REDIS_CHANNEL, JSON.stringify(redisMessage)); + } catch (error) { + logger.error("Failed to broadcast message via Redis, remote nodes may not receive it:", error); + // Continue execution - local broadcast already completed + } + } else { + logger.debug("Redis unavailable - broadcast limited to local node only"); + } +}; + +// Check if a client has active connections across all nodes +const hasActiveConnections = async (clientId: string): Promise => { + if (!redisManager.isRedisEnabled()) { + const mapKey = getClientMapKey(clientId); + const clients = connectedClients.get(mapKey); + return !!(clients && clients.length > 0); + } + + const activeNodes = await redisManager.smembers( + getConnectionsKey(clientId) + ); + return activeNodes.length > 0; +}; + +// Get all active nodes for a client +const getActiveNodes = async ( + clientType: ClientType, + clientId: string +): Promise => { + if (!redisManager.isRedisEnabled()) { + const mapKey = getClientMapKey(clientId); + const clients = connectedClients.get(mapKey); + return clients && clients.length > 0 ? [NODE_ID] : []; + } + + return await redisManager.smembers(getConnectionsKey(clientId)); +}; + +// Token verification middleware +const verifyToken = async ( + token: string, + clientType: ClientType +): Promise => { + try { + if (clientType === "newt") { + const { session, newt } = await validateNewtSessionToken(token); + if (!session || !newt) { + return null; + } + const existingNewt = await db + .select() + .from(newts) + .where(eq(newts.newtId, newt.newtId)); + if (!existingNewt || !existingNewt[0]) { + return null; + } + return { client: existingNewt[0], session, clientType }; + } else if (clientType === "olm") { + const { session, olm } = await validateOlmSessionToken(token); + if (!session || !olm) { + return null; + } + const existingOlm = await db + .select() + .from(olms) + .where(eq(olms.olmId, olm.olmId)); + if (!existingOlm || !existingOlm[0]) { + return null; + } + return { client: existingOlm[0], session, clientType }; + } else if (clientType === "remoteExitNode") { + const { session, remoteExitNode } = + await validateRemoteExitNodeSessionToken(token); + if (!session || !remoteExitNode) { + return null; + } + const existingRemoteExitNode = await db + .select() + .from(remoteExitNodes) + .where( + eq( + remoteExitNodes.remoteExitNodeId, + remoteExitNode.remoteExitNodeId + ) + ); + if (!existingRemoteExitNode || !existingRemoteExitNode[0]) { + return null; + } + return { client: existingRemoteExitNode[0], session, clientType }; + } + + return null; + } catch (error) { + logger.error("Token verification failed:", error); + return null; + } +}; + +const setupConnection = async ( + ws: AuthenticatedWebSocket, + client: Newt | Olm | RemoteExitNode, + clientType: ClientType +): Promise => { + logger.info("Establishing websocket connection"); + if (!client) { + logger.error("Connection attempt without client"); + return ws.terminate(); + } + + ws.client = client; + ws.clientType = clientType; + ws.isFullyConnected = false; + ws.pendingMessages = []; + + // Get client ID first + let clientId: string; + if (clientType === "newt") { + clientId = (client as Newt).newtId; + } else if (clientType === "olm") { + clientId = (client as Olm).olmId; + } else if (clientType === "remoteExitNode") { + clientId = (client as RemoteExitNode).remoteExitNodeId; + } else { + throw new Error(`Unknown client type: ${clientType}`); + } + + // Set up message handler FIRST to prevent race condition + ws.on("message", async (data) => { + if (!ws.isFullyConnected) { + // Queue message for later processing with limits + ws.pendingMessages = ws.pendingMessages || []; + + if (ws.pendingMessages.length >= MAX_PENDING_MESSAGES) { + logger.warn( + `Too many pending messages for ${clientType.toUpperCase()} ID: ${clientId}, dropping oldest message` + ); + ws.pendingMessages.shift(); // Remove oldest message + } + + logger.debug( + `Queueing message from ${clientType.toUpperCase()} ID: ${clientId} (connection not fully established)` + ); + ws.pendingMessages.push(data as Buffer); + return; + } + + await processMessage(ws, data as Buffer, clientId, clientType); + }); + + // Set up other event handlers before async operations + ws.on("close", async () => { + // Clear any pending messages to prevent memory leaks + if (ws.pendingMessages) { + ws.pendingMessages = []; + } + await removeClient(clientType, clientId, ws); + logger.info( + `Client disconnected - ${clientType.toUpperCase()} ID: ${clientId}` + ); + }); + + ws.on("error", (error: Error) => { + logger.error( + `WebSocket error for ${clientType.toUpperCase()} ID ${clientId}:`, + error + ); + }); + + try { + await addClient(clientType, clientId, ws); + + // Mark connection as fully established + ws.isFullyConnected = true; + + logger.info( + `WebSocket connection fully established and ready - ${clientType.toUpperCase()} ID: ${clientId}` + ); + + // Process any messages that were queued while connection was being established + await processPendingMessages(ws, clientId, clientType); + } catch (error) { + logger.error( + `Failed to fully establish connection for ${clientType.toUpperCase()} ID: ${clientId}:`, + error + ); + // ws.send(JSON.stringify({ + // type: "connection_error", + // data: { + // message: "Failed to establish connection" + // } + // })); + ws.terminate(); + return; + } +}; + +// Router endpoint +router.get("/ws", (req: Request, res: Response) => { + res.status(200).send("WebSocket endpoint"); +}); + +// WebSocket upgrade handler +const handleWSUpgrade = (server: HttpServer): void => { + server.on( + "upgrade", + async (request: WebSocketRequest, socket: Socket, head: Buffer) => { + try { + const url = new URL( + request.url || "", + `http://${request.headers.host}` + ); + const token = + url.searchParams.get("token") || + request.headers["sec-websocket-protocol"] || + ""; + let clientType = url.searchParams.get( + "clientType" + ) as ClientType; + + if (!clientType) { + clientType = "newt"; + } + + if ( + !token || + !clientType || + !["newt", "olm", "remoteExitNode"].includes(clientType) + ) { + logger.warn( + "Unauthorized connection attempt: invalid token or client type..." + ); + socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); + socket.destroy(); + return; + } + + const tokenPayload = await verifyToken(token, clientType); + if (!tokenPayload) { + logger.debug( + "Unauthorized connection attempt: invalid token..." + ); + socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); + socket.destroy(); + return; + } + + wss.handleUpgrade( + request, + socket, + head, + (ws: AuthenticatedWebSocket) => { + setupConnection( + ws, + tokenPayload.client, + tokenPayload.clientType + ); + } + ); + } catch (error) { + logger.error("WebSocket upgrade error:", error); + socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + socket.destroy(); + } + } + ); +}; + +// Add periodic connection state sync to handle Redis disconnections/reconnections +const startPeriodicStateSync = (): void => { + // Lightweight sync every 5 minutes - just restore our own state + setInterval(async () => { + if (redisManager.isRedisEnabled() && !isRedisRecoveryInProgress) { + try { + await restoreLocalConnectionsToRedis(); + logger.debug("Periodic connection state sync completed"); + } catch (error) { + logger.error("Error during periodic connection state sync:", error); + } + } + }, 5 * 60 * 1000); // 5 minutes + + // Cleanup stale connections every 15 minutes + setInterval(async () => { + if (redisManager.isRedisEnabled()) { + try { + await cleanupStaleConnections(); + logger.debug("Periodic connection cleanup completed"); + } catch (error) { + logger.error("Error during periodic connection cleanup:", error); + } + } + }, 15 * 60 * 1000); // 15 minutes +}; + +const cleanupStaleConnections = async (): Promise => { + if (!redisManager.isRedisEnabled()) return; + + try { + const nodeKeys = await redisManager.getClient()?.keys(`ws:node:${NODE_ID}:*`) || []; + + for (const nodeKey of nodeKeys) { + const connections = await redisManager.hgetall(nodeKey); + const clientId = nodeKey.replace(`ws:node:${NODE_ID}:`, ''); + const localClients = connectedClients.get(clientId) || []; + const localConnectionIds = localClients + .filter(client => client.readyState === WebSocket.OPEN) + .map(client => client.connectionId) + .filter(Boolean); + + // Remove Redis entries for connections that no longer exist locally + for (const [connectionId, timestamp] of Object.entries(connections)) { + if (!localConnectionIds.includes(connectionId)) { + await redisManager.hdel(nodeKey, connectionId); + logger.debug(`Cleaned up stale connection: ${connectionId} for client: ${clientId}`); + } + } + + // If no connections remain for this client, remove from Redis entirely + const remainingConnections = await redisManager.hgetall(nodeKey); + if (Object.keys(remainingConnections).length === 0) { + await redisManager.srem(getConnectionsKey(clientId), NODE_ID); + await redisManager.del(nodeKey); + logger.debug(`Cleaned up empty connection tracking for client: ${clientId}`); + } + } + } catch (error) { + logger.error("Error cleaning up stale connections:", error); + } +}; + +// Initialize Redis subscription when the module is loaded +if (redisManager.isRedisEnabled()) { + initializeRedisSubscription().catch((error) => { + logger.error("Failed to initialize Redis subscription:", error); + }); + + // Register recovery callback with Redis manager + // When Redis reconnects, each node simply restores its own local state + redisManager.onReconnection(async () => { + logger.info("Redis reconnected, starting WebSocket state recovery..."); + await recoverConnectionState(); + }); + + // Start periodic state synchronization + startPeriodicStateSync(); + + logger.info( + `WebSocket handler initialized with Redis support - Node ID: ${NODE_ID}` + ); +} else { + logger.debug( + "WebSocket handler initialized in local mode" + ); +} + +// Cleanup function for graceful shutdown +const cleanup = async (): Promise => { + try { + // Close all WebSocket connections + connectedClients.forEach((clients) => { + clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.terminate(); + } + }); + }); + + // Clean up Redis tracking for this node + if (redisManager.isRedisEnabled()) { + const keys = + (await redisManager + .getClient() + ?.keys(`ws:node:${NODE_ID}:*`)) || []; + if (keys.length > 0) { + await Promise.all(keys.map((key) => redisManager.del(key))); + } + } + + logger.info("WebSocket cleanup completed"); + } catch (error) { + logger.error("Error during WebSocket cleanup:", error); + } +}; + +// Handle process termination +process.on("SIGTERM", cleanup); +process.on("SIGINT", cleanup); + +export { + router, + handleWSUpgrade, + sendToClient, + broadcastToAllExcept, + connectedClients, + hasActiveConnections, + getActiveNodes, + NODE_ID, + cleanup +}; diff --git a/server/setup/clearStaleData.ts b/server/setup/clearStaleData.ts index 220a64f5..2e54656c 100644 --- a/server/setup/clearStaleData.ts +++ b/server/setup/clearStaleData.ts @@ -1,4 +1,5 @@ -import { db } from "@server/db"; +import { build } from "@server/build"; +import { db, sessionTransferToken } from "@server/db"; import { emailVerificationCodes, newtSessions, @@ -76,4 +77,16 @@ export async function clearStaleData() { } catch (e) { logger.warn("Error clearing expired resourceOtp:", e); } + + if (build !== "oss") { + try { + await db + .delete(sessionTransferToken) + .where( + lt(sessionTransferToken.expiresAt, new Date().getTime()) + ); + } catch (e) { + logger.warn("Error clearing expired sessionTransferToken:", e); + } + } } diff --git a/server/setup/migrationsSqlite.ts b/server/setup/migrationsSqlite.ts index d7c6793f..80d2139d 100644 --- a/server/setup/migrationsSqlite.ts +++ b/server/setup/migrationsSqlite.ts @@ -136,7 +136,7 @@ async function executeScripts() { const pendingMigrations = lastExecuted .map((m) => m) .sort((a, b) => semver.compare(b.version, a.version)); - const startVersion = pendingMigrations[0]?.version ?? "0.0.0"; + const startVersion = pendingMigrations[0]?.version ?? APP_VERSION; console.log(`Starting migrations from version ${startVersion}`); const migrationsToRun = migrations.filter((migration) => diff --git a/src/actions/server.ts b/src/actions/server.ts index 27d6b562..655cbc63 100644 --- a/src/actions/server.ts +++ b/src/actions/server.ts @@ -1,6 +1,6 @@ "use server"; -import { cookies } from "next/headers"; +import { cookies, headers as reqHeaders } from "next/headers"; import { ResponseT } from "@server/types/Response"; import { pullEnv } from "@app/lib/pullEnv"; @@ -14,7 +14,10 @@ type CookieOptions = { domain?: string; }; -function parseSetCookieString(setCookie: string): { +function parseSetCookieString( + setCookie: string, + host?: string +): { name: string; value: string; options: CookieOptions; @@ -50,16 +53,11 @@ function parseSetCookieString(setCookie: string): { case "max-age": options.maxAge = parseInt(v, 10); break; - case "domain": - options.domain = v; - break; } } if (!options.domain) { - const d = env.app.dashboardUrl - ? "." + new URL(env.app.dashboardUrl).hostname - : undefined; + const d = host ? host : new URL(env.app.dashboardUrl).hostname; if (d) { options.domain = d; } @@ -78,6 +76,9 @@ async function makeApiRequest( const allCookies = await cookies(); const cookieHeader = allCookies.toString(); + const headersList = await reqHeaders(); + const host = headersList.get("host"); + const headers: Record = { "Content-Type": "application/json", "X-CSRF-Token": "x-csrf-protection", @@ -107,7 +108,10 @@ async function makeApiRequest( const rawSetCookie = res.headers.get("set-cookie"); if (rawSetCookie) { try { - const { name, value, options } = parseSetCookieString(rawSetCookie); + const { name, value, options } = parseSetCookieString( + rawSetCookie, + host || undefined + ); const allCookies = await cookies(); allCookies.set(name, value, options); } catch (cookieError) { diff --git a/src/app/[orgId]/layout.tsx b/src/app/[orgId]/layout.tsx index 3ab0b92e..ecc7de01 100644 --- a/src/app/[orgId]/layout.tsx +++ b/src/app/[orgId]/layout.tsx @@ -9,6 +9,10 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; import SetLastOrgCookie from "@app/components/SetLastOrgCookie"; +import PrivateSubscriptionStatusProvider from "@app/providers/PrivateSubscriptionStatusProvider"; +import { GetOrgSubscriptionResponse } from "@server/routers/private/billing/getOrgSubscription"; +import { pullEnv } from "@app/lib/pullEnv"; +import { build } from "@server/build"; export default async function OrgLayout(props: { children: React.ReactNode; @@ -17,6 +21,7 @@ export default async function OrgLayout(props: { const cookie = await authCookieHeader(); const params = await props.params; const orgId = params.orgId; + const env = pullEnv(); if (!orgId) { redirect(`/`); @@ -50,10 +55,31 @@ export default async function OrgLayout(props: { redirect(`/`); } + let subscriptionStatus = null; + if (build != "oss") { + try { + const getSubscription = cache(() => + internal.get>( + `/org/${orgId}/billing/subscription`, + cookie + ) + ); + const subRes = await getSubscription(); + subscriptionStatus = subRes.data.data; + } catch (error) { + // If subscription fetch fails, keep subscriptionStatus as null + console.error("Failed to fetch subscription status:", error); + } + } + return ( - <> + {props.children} - + ); } diff --git a/src/app/[orgId]/settings/(private)/billing/layout.tsx b/src/app/[orgId]/settings/(private)/billing/layout.tsx new file mode 100644 index 00000000..8559ddf4 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/billing/layout.tsx @@ -0,0 +1,97 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { internal } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { HorizontalTabs } from "@app/components/HorizontalTabs"; +import { verifySession } from "@app/lib/auth/verifySession"; +import OrgProvider from "@app/providers/OrgProvider"; +import OrgUserProvider from "@app/providers/OrgUserProvider"; +import { GetOrgResponse } from "@server/routers/org"; +import { GetOrgUserResponse } from "@server/routers/user"; +import { AxiosResponse } from "axios"; +import { redirect } from "next/navigation"; +import { cache } from "react"; +import { getTranslations } from 'next-intl/server'; + +type BillingSettingsProps = { + children: React.ReactNode; + params: Promise<{ orgId: string }>; +}; + +export default async function BillingSettingsPage({ + children, + params, +}: BillingSettingsProps) { + const { orgId } = await params; + + const getUser = cache(verifySession); + const user = await getUser(); + + if (!user) { + redirect(`/`); + } + + let orgUser = null; + try { + const getOrgUser = cache(async () => + internal.get>( + `/org/${orgId}/user/${user.userId}`, + await authCookieHeader(), + ), + ); + const res = await getOrgUser(); + orgUser = res.data.data; + } catch { + redirect(`/${orgId}`); + } + + let org = null; + try { + const getOrg = cache(async () => + internal.get>( + `/org/${orgId}`, + await authCookieHeader(), + ), + ); + const res = await getOrg(); + org = res.data.data; + } catch { + redirect(`/${orgId}`); + } + + const t = await getTranslations(); + + const navItems = [ + { + title: t('billing'), + href: `/{orgId}/settings/billing`, + }, + ]; + + return ( + <> + + + + + {children} + + + + ); +} diff --git a/src/app/[orgId]/settings/(private)/billing/page.tsx b/src/app/[orgId]/settings/(private)/billing/page.tsx new file mode 100644 index 00000000..1270c30f --- /dev/null +++ b/src/app/[orgId]/settings/(private)/billing/page.tsx @@ -0,0 +1,767 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { Button } from "@app/components/ui/button"; +import { useOrgContext } from "@app/hooks/useOrgContext"; +import { toast } from "@app/hooks/useToast"; +import { useState, useEffect } from "react"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { formatAxiosError } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { + SettingsContainer, + SettingsSection, + SettingsSectionHeader, + SettingsSectionTitle, + SettingsSectionDescription, + SettingsSectionBody, + SettingsSectionFooter +} from "@app/components/Settings"; +import { Badge } from "@/components/ui/badge"; +import { Separator } from "@/components/ui/separator"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Progress } from "@/components/ui/progress"; +import { + CreditCard, + Database, + Clock, + AlertCircle, + CheckCircle, + Users, + Calculator, + ExternalLink, + Gift, + Server +} from "lucide-react"; +import { InfoPopup } from "@/components/ui/info-popup"; +import { + GetOrgSubscriptionResponse, + GetOrgUsageResponse +} from "@server/routers/private/billing"; +import { useTranslations } from "use-intl"; +import Link from "next/link"; + +export default function GeneralPage() { + const { org } = useOrgContext(); + const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + // Subscription state + const [subscription, setSubscription] = + useState(null); + const [subscriptionItems, setSubscriptionItems] = useState< + GetOrgSubscriptionResponse["items"] + >([]); + const [subscriptionLoading, setSubscriptionLoading] = useState(true); + + // Example usage data (replace with real usage data if available) + const [usageData, setUsageData] = useState( + [] + ); + const [limitsData, setLimitsData] = useState( + [] + ); + + useEffect(() => { + async function fetchSubscription() { + setSubscriptionLoading(true); + try { + const res = await api.get< + AxiosResponse + >(`/org/${org.org.orgId}/billing/subscription`); + const { subscription, items } = res.data.data; + setSubscription(subscription); + setSubscriptionItems(items); + setHasSubscription( + !!subscription && subscription.status === "active" + ); + } catch (error) { + toast({ + title: t("billingFailedToLoadSubscription"), + description: formatAxiosError(error), + variant: "destructive" + }); + } finally { + setSubscriptionLoading(false); + } + } + fetchSubscription(); + }, [org.org.orgId]); + + useEffect(() => { + async function fetchUsage() { + try { + const res = await api.get>( + `/org/${org.org.orgId}/billing/usage` + ); + const { usage, limits } = res.data.data; + + setUsageData(usage); + setLimitsData(limits); + } catch (error) { + toast({ + title: t("billingFailedToLoadUsage"), + description: formatAxiosError(error), + variant: "destructive" + }); + } finally { + } + } + fetchUsage(); + }, [org.org.orgId]); + + const [hasSubscription, setHasSubscription] = useState(true); + const [isLoading, setIsLoading] = useState(false); + // const [newPricing, setNewPricing] = useState({ + // pricePerGB: mockSubscription.pricePerGB, + // pricePerMinute: mockSubscription.pricePerMinute, + // }) + + const handleStartSubscription = async () => { + setIsLoading(true); + try { + const response = await api.post>( + `/org/${org.org.orgId}/billing/create-checkout-session`, + {} + ); + console.log("Checkout session response:", response.data); + const checkoutUrl = response.data.data; + if (checkoutUrl) { + window.location.href = checkoutUrl; + } else { + toast({ + title: t("billingFailedToGetCheckoutUrl"), + description: t("billingPleaseTryAgainLater"), + variant: "destructive" + }); + setIsLoading(false); + } + } catch (error) { + toast({ + title: t("billingCheckoutError"), + description: formatAxiosError(error), + variant: "destructive" + }); + setIsLoading(false); + } + }; + + const handleModifySubscription = async () => { + setIsLoading(true); + try { + const response = await api.post>( + `/org/${org.org.orgId}/billing/create-portal-session`, + {} + ); + const portalUrl = response.data.data; + if (portalUrl) { + window.location.href = portalUrl; + } else { + toast({ + title: t("billingFailedToGetPortalUrl"), + description: t("billingPleaseTryAgainLater"), + variant: "destructive" + }); + setIsLoading(false); + } + } catch (error) { + toast({ + title: t("billingPortalError"), + description: formatAxiosError(error), + variant: "destructive" + }); + setIsLoading(false); + } + }; + + // Usage IDs + const SITE_UPTIME = "siteUptime"; + const USERS = "users"; + const EGRESS_DATA_MB = "egressDataMb"; + const DOMAINS = "domains"; + const REMOTE_EXIT_NODES = "remoteExitNodes"; + + // Helper to calculate tiered price + function calculateTieredPrice( + usage: number, + tiersRaw: string | null | undefined + ) { + if (!tiersRaw) return 0; + let tiers: any[] = []; + try { + tiers = JSON.parse(tiersRaw); + } catch { + return 0; + } + let total = 0; + let remaining = usage; + for (const tier of tiers) { + const upTo = tier.up_to === null ? Infinity : Number(tier.up_to); + const unitAmount = + tier.unit_amount !== null + ? Number(tier.unit_amount / 100) + : tier.unit_amount_decimal + ? Number(tier.unit_amount_decimal / 100) + : 0; + const tierQty = Math.min( + remaining, + upTo === Infinity ? remaining : upTo - (usage - remaining) + ); + if (tierQty > 0) { + total += tierQty * unitAmount; + remaining -= tierQty; + } + if (remaining <= 0) break; + } + return total; + } + + function getDisplayPrice(tiersRaw: string | null | undefined) { + //find the first non-zero tier price + if (!tiersRaw) return "$0.00"; + let tiers: any[] = []; + try { + tiers = JSON.parse(tiersRaw); + } catch { + return "$0.00"; + } + if (tiers.length === 0) return "$0.00"; + + // find the first tier with a non-zero price + const firstTier = + tiers.find( + (t) => + t.unit_amount > 0 || + (t.unit_amount_decimal && Number(t.unit_amount_decimal) > 0) + ) || tiers[0]; + const unitAmount = + firstTier.unit_amount !== null + ? Number(firstTier.unit_amount / 100) + : firstTier.unit_amount_decimal + ? Number(firstTier.unit_amount_decimal / 100) + : 0; + return `$${unitAmount.toFixed(4)}`; // ${firstTier.up_to === null ? "per unit" : `per ${firstTier.up_to} units`}`; + } + + // Helper to get included usage amount from subscription tier + function getIncludedUsage(tiersRaw: string | null | undefined) { + if (!tiersRaw) return 0; + let tiers: any[] = []; + try { + tiers = JSON.parse(tiersRaw); + } catch { + return 0; + } + if (tiers.length === 0) return 0; + + // Find the first tier (which represents included usage) + const firstTier = tiers[0]; + if (!firstTier) return 0; + + // If the first tier has a unit_amount of 0, it represents included usage + const isIncludedTier = + (firstTier.unit_amount === 0 || firstTier.unit_amount === null) && + (!firstTier.unit_amount_decimal || + Number(firstTier.unit_amount_decimal) === 0); + + if (isIncludedTier && firstTier.up_to !== null) { + return Number(firstTier.up_to); + } + + return 0; + } + + // Helper to get display value for included usage + function getIncludedUsageDisplay(includedAmount: number, usageType: any) { + if (includedAmount === 0) return "0"; + + if (usageType.id === EGRESS_DATA_MB) { + // Convert MB to GB for data usage + return (includedAmount / 1000).toFixed(2); + } + + if (usageType.id === USERS || usageType.id === DOMAINS) { + // divide by 32 days + return (includedAmount / 32).toFixed(2); + } + + return includedAmount.toString(); + } + + // Helper to get usage, subscription item, and limit by usageId + function getUsageItemAndLimit( + usageData: any[], + subscriptionItems: any[], + limitsData: any[], + usageId: string + ) { + const usage = usageData.find((u) => u.featureId === usageId); + if (!usage) return { usage: 0, item: undefined, limit: undefined }; + const item = subscriptionItems.find((i) => i.meterId === usage.meterId); + const limit = limitsData.find((l) => l.featureId === usageId); + return { usage: usage ?? 0, item, limit }; + } + + // Helper to check if usage exceeds limit + function isOverLimit(usage: any, limit: any, usageType: any) { + if (!limit || !usage) return false; + const currentUsage = usageType.getLimitUsage(usage); + return currentUsage > limit.value; + } + + // Map usage and pricing for each usage type + const usageTypes = [ + { + id: EGRESS_DATA_MB, + label: t("billingDataUsage"), + icon: , + unit: "GB", + unitRaw: "MB", + info: t("billingDataUsageInfo"), + note: "Not counted on self-hosted nodes", + // Convert MB to GB for display and pricing + getDisplay: (v: any) => (v.latestValue / 1000).toFixed(2), + getLimitDisplay: (v: any) => (v.value / 1000).toFixed(2), + getUsage: (v: any) => v.latestValue, + getLimitUsage: (v: any) => v.latestValue + }, + { + id: SITE_UPTIME, + label: t("billingOnlineTime"), + icon: , + unit: "min", + info: t("billingOnlineTimeInfo"), + note: "Not counted on self-hosted nodes", + getDisplay: (v: any) => v.latestValue, + getLimitDisplay: (v: any) => v.value, + getUsage: (v: any) => v.latestValue, + getLimitUsage: (v: any) => v.latestValue + }, + { + id: USERS, + label: t("billingUsers"), + icon: , + unit: "", + unitRaw: "user days", + info: t("billingUsersInfo"), + getDisplay: (v: any) => v.instantaneousValue, + getLimitDisplay: (v: any) => v.value, + getUsage: (v: any) => v.latestValue, + getLimitUsage: (v: any) => v.instantaneousValue + }, + { + id: DOMAINS, + label: t("billingDomains"), + icon: , + unit: "", + unitRaw: "domain days", + info: t("billingDomainInfo"), + getDisplay: (v: any) => v.instantaneousValue, + getLimitDisplay: (v: any) => v.value, + getUsage: (v: any) => v.latestValue, + getLimitUsage: (v: any) => v.instantaneousValue + }, + { + id: REMOTE_EXIT_NODES, + label: t("billingRemoteExitNodes"), + icon: , + unit: "", + unitRaw: "node days", + info: t("billingRemoteExitNodesInfo"), + getDisplay: (v: any) => v.instantaneousValue, + getLimitDisplay: (v: any) => v.value, + getUsage: (v: any) => v.latestValue, + getLimitUsage: (v: any) => v.instantaneousValue + } + ]; + + if (subscriptionLoading) { + return ( +
+ {t("billingLoadingSubscription")} +
+ ); + } + + return ( + +
+ + {subscription?.status === "active" && ( + + )} + {subscription + ? subscription.status.charAt(0).toUpperCase() + + subscription.status.slice(1) + : t("billingFreeTier")} + + + {t("billingPricingCalculatorLink")} + + +
+ + {usageTypes.some((type) => { + const { usage, limit } = getUsageItemAndLimit( + usageData, + subscriptionItems, + limitsData, + type.id + ); + return isOverLimit(usage, limit, type); + }) && ( + + + + {t("billingWarningOverLimit")} + + + )} + + + + + {t("billingUsageLimitsOverview")} + + + {t("billingMonitorUsage")} + + + +
+ {usageTypes.map((type) => { + const { usage, limit } = getUsageItemAndLimit( + usageData, + subscriptionItems, + limitsData, + type.id + ); + const displayUsage = type.getDisplay(usage); + const usageForPricing = type.getLimitUsage(usage); + const overLimit = isOverLimit(usage, limit, type); + const percentage = limit + ? Math.min( + (usageForPricing / limit.value) * 100, + 100 + ) + : 0; + + return ( +
+
+
+ {type.icon} + + {type.label} + + +
+
+ + {displayUsage} {type.unit} + + {limit && ( + + {" "} + /{" "} + {type.getLimitDisplay( + limit + )}{" "} + {type.unit} + + )} +
+
+ {type.note && ( +
+ {type.note} +
+ )} + {limit && ( + 80 + ? "warning" + : "success" + } + /> + )} + {!limit && ( +

+ {t("billingNoLimitConfigured")} +

+ )} +
+ ); + })} +
+
+
+ + {(hasSubscription || + (!hasSubscription && limitsData.length > 0)) && ( + + + + {t("billingIncludedUsage")} + + + {hasSubscription + ? t("billingIncludedUsageDescription") + : t("billingFreeTierIncludedUsage")} + + + +
+ {usageTypes.map((type) => { + const { item, limit } = getUsageItemAndLimit( + usageData, + subscriptionItems, + limitsData, + type.id + ); + + // For subscribed users, show included usage from tiers + // For free users, show the limit as "included" + let includedAmount = 0; + let displayIncluded = "0"; + + if (hasSubscription && item) { + includedAmount = getIncludedUsage( + item.tiers + ); + displayIncluded = getIncludedUsageDisplay( + includedAmount, + type + ); + } else if ( + !hasSubscription && + limit && + limit.value > 0 + ) { + // Show free tier limits as "included" + includedAmount = limit.value; + displayIncluded = + type.getLimitDisplay(limit); + } + + if (includedAmount === 0) return null; + + return ( +
+
+ {type.icon} + + {type.label} + +
+
+
+ {hasSubscription ? ( + + ) : ( + + )} + + {displayIncluded}{" "} + {type.unit} + +
+
+ {hasSubscription + ? t("billingIncluded") + : t("billingFreeTier")} +
+
+
+ ); + })} +
+
+
+ )} + + {hasSubscription && ( + + + + {t("billingEstimatedPeriod")} + + + +
+
+ {usageTypes.map((type) => { + const { usage, item } = + getUsageItemAndLimit( + usageData, + subscriptionItems, + limitsData, + type.id + ); + const displayPrice = getDisplayPrice( + item?.tiers + ); + return ( +
+ {type.label}: + + {type.getUsage(usage)}{" "} + {type.unitRaw || type.unit} x{" "} + {displayPrice} + +
+ ); + })} + {/* Show recurring charges (items with unitAmount but no tiers/meterId) */} + {subscriptionItems + .filter( + (item) => + item.unitAmount && + item.unitAmount > 0 && + !item.tiers && + !item.meterId + ) + .map((item, index) => ( +
+ + {item.name || + t("billingRecurringCharge")} + : + + + $ + {( + (item.unitAmount || 0) / 100 + ).toFixed(2)} + +
+ ))} + +
+ {t("billingEstimatedTotal")} + + $ + {( + usageTypes.reduce((sum, type) => { + const { usage, item } = + getUsageItemAndLimit( + usageData, + subscriptionItems, + limitsData, + type.id + ); + const usageForPricing = + type.getUsage(usage); + const cost = item + ? calculateTieredPrice( + usageForPricing, + item.tiers + ) + : 0; + return sum + cost; + }, 0) + + // Add recurring charges + subscriptionItems + .filter( + (item) => + item.unitAmount && + item.unitAmount > 0 && + !item.tiers && + !item.meterId + ) + .reduce( + (sum, item) => + sum + + (item.unitAmount || 0) / + 100, + 0 + ) + ).toFixed(2)} + +
+
+
+

+ {t("billingNotes")} +

+
+

{t("billingEstimateNote")}

+

{t("billingActualChargesMayVary")}

+

{t("billingBilledAtEnd")}

+
+
+
+ + + + +
+
+ )} + + {!hasSubscription && ( + + +
+ +

+ {t("billingNoActiveSubscription")} +

+ +
+
+
+ )} +
+ ); +} diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx new file mode 100644 index 00000000..59f7aa85 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -0,0 +1,996 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { Button } from "@app/components/ui/button"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import { Input } from "@app/components/ui/input"; +import { useForm } from "react-hook-form"; +import { toast } from "@app/hooks/useToast"; +import { useRouter, useParams, redirect } from "next/navigation"; +import { + SettingsContainer, + SettingsSection, + SettingsSectionHeader, + SettingsSectionTitle, + SettingsSectionDescription, + SettingsSectionBody, + SettingsSectionForm, + SettingsSectionFooter, + SettingsSectionGrid +} from "@app/components/Settings"; +import { formatAxiosError } from "@app/lib/api"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useState, useEffect } from "react"; +import { SwitchInput } from "@app/components/SwitchInput"; +import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; +import { InfoIcon, ExternalLink } from "lucide-react"; +import { + InfoSection, + InfoSectionContent, + InfoSections, + InfoSectionTitle +} from "@app/components/InfoSection"; +import CopyToClipboard from "@app/components/CopyToClipboard"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import IdpTypeBadge from "@app/components/IdpTypeBadge"; +import { useTranslations } from "next-intl"; +import { AxiosResponse } from "axios"; +import { ListRolesResponse } from "@server/routers/role"; +import AutoProvisionConfigWidget from "@app/components/private/AutoProvisionConfigWidget"; + +export default function GeneralPage() { + const { env } = useEnvContext(); + const api = createApiClient({ env }); + const router = useRouter(); + const { idpId, orgId } = useParams(); + const [loading, setLoading] = useState(false); + const [initialLoading, setInitialLoading] = useState(true); + const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]); + const [roleMappingMode, setRoleMappingMode] = useState< + "role" | "expression" + >("role"); + const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc"); + const { isUnlocked } = useLicenseStatusContext(); + + const [redirectUrl, setRedirectUrl] = useState( + `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback` + ); + const t = useTranslations(); + + // OIDC form schema (full configuration) + const OidcFormSchema = z.object({ + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z + .string() + .min(1, { message: t("idpClientSecretRequired") }), + roleMapping: z.string().nullable().optional(), + roleId: z.number().nullable().optional(), + authUrl: z.string().url({ message: t("idpErrorAuthUrlInvalid") }), + tokenUrl: z.string().url({ message: t("idpErrorTokenUrlInvalid") }), + identifierPath: z.string().min(1, { message: t("idpPathRequired") }), + emailPath: z.string().nullable().optional(), + namePath: z.string().nullable().optional(), + scopes: z.string().min(1, { message: t("idpScopeRequired") }), + autoProvision: z.boolean().default(false) + }); + + // Google form schema (simplified) + const GoogleFormSchema = z.object({ + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z + .string() + .min(1, { message: t("idpClientSecretRequired") }), + roleMapping: z.string().nullable().optional(), + roleId: z.number().nullable().optional(), + autoProvision: z.boolean().default(false) + }); + + // Azure form schema (simplified with tenant ID) + const AzureFormSchema = z.object({ + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z + .string() + .min(1, { message: t("idpClientSecretRequired") }), + tenantId: z.string().min(1, { message: t("idpTenantIdRequired") }), + roleMapping: z.string().nullable().optional(), + roleId: z.number().nullable().optional(), + autoProvision: z.boolean().default(false) + }); + + type OidcFormValues = z.infer; + type GoogleFormValues = z.infer; + type AzureFormValues = z.infer; + type GeneralFormValues = + | OidcFormValues + | GoogleFormValues + | AzureFormValues; + + // Get the appropriate schema based on variant + const getFormSchema = () => { + switch (variant) { + case "google": + return GoogleFormSchema; + case "azure": + return AzureFormSchema; + default: + return OidcFormSchema; + } + }; + + const form = useForm({ + resolver: zodResolver(getFormSchema()) as any, // is this right? + defaultValues: { + name: "", + clientId: "", + clientSecret: "", + authUrl: "", + tokenUrl: "", + identifierPath: "sub", + emailPath: "email", + namePath: "name", + scopes: "openid profile email", + autoProvision: true, + roleMapping: null, + roleId: null, + tenantId: "" + } + }); + + // Update form resolver when variant changes + useEffect(() => { + form.clearErrors(); + // Note: We can't change the resolver dynamically, so we'll handle validation in onSubmit + }, [variant]); + + useEffect(() => { + async function fetchRoles() { + const res = await api + .get>(`/org/${orgId}/roles`) + .catch((e) => { + console.error(e); + toast({ + variant: "destructive", + title: t("accessRoleErrorFetch"), + description: formatAxiosError( + e, + t("accessRoleErrorFetchDescription") + ) + }); + }); + + if (res?.status === 200) { + setRoles(res.data.data.roles); + } + } + + const loadIdp = async ( + availableRoles: { roleId: number; name: string }[] + ) => { + try { + const res = await api.get(`/org/${orgId}/idp/${idpId}`); + if (res.status === 200) { + const data = res.data.data; + const roleMapping = data.idpOrg.roleMapping; + const idpVariant = data.idpOidcConfig?.variant || "oidc"; + setRedirectUrl(res.data.data.redirectUrl); + + // Set the variant + setVariant(idpVariant as "oidc" | "google" | "azure"); + + // Check if roleMapping matches the basic pattern '{role name}' (simple single role) + // This should NOT match complex expressions like 'Admin' || 'Member' + const isBasicRolePattern = + roleMapping && + typeof roleMapping === "string" && + /^'[^']+'$/.test(roleMapping); + + // Determine if roleMapping is a number (roleId) or matches basic pattern + const isRoleId = + !isNaN(Number(roleMapping)) && roleMapping !== ""; + const isRoleName = isBasicRolePattern; + + // Extract role name from basic pattern for matching + let extractedRoleName = null; + if (isRoleName) { + extractedRoleName = roleMapping.slice(1, -1); // Remove quotes + } + + // Try to find matching role by name if we have a basic pattern + let matchingRoleId = undefined; + if (extractedRoleName && availableRoles.length > 0) { + const matchingRole = availableRoles.find( + (role) => role.name === extractedRoleName + ); + if (matchingRole) { + matchingRoleId = matchingRole.roleId; + } + } + + // Extract tenant ID from Azure URLs if present + let tenantId = ""; + if (idpVariant === "azure" && data.idpOidcConfig?.authUrl) { + // Azure URL format: https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize + console.log( + "Azure authUrl:", + data.idpOidcConfig.authUrl + ); + const tenantMatch = data.idpOidcConfig.authUrl.match( + /login\.microsoftonline\.com\/([^\/]+)\/oauth2/ + ); + console.log("Tenant match:", tenantMatch); + if (tenantMatch) { + tenantId = tenantMatch[1]; + console.log("Extracted tenantId:", tenantId); + } + } + + // Reset form with appropriate data based on variant + const formData: any = { + name: data.idp.name, + clientId: data.idpOidcConfig.clientId, + clientSecret: data.idpOidcConfig.clientSecret, + autoProvision: data.idp.autoProvision, + roleMapping: roleMapping || null, + roleId: isRoleId + ? Number(roleMapping) + : matchingRoleId || null + }; + + console.log(formData); + + // Add variant-specific fields + if (idpVariant === "oidc") { + formData.authUrl = data.idpOidcConfig.authUrl; + formData.tokenUrl = data.idpOidcConfig.tokenUrl; + formData.identifierPath = + data.idpOidcConfig.identifierPath; + formData.emailPath = + data.idpOidcConfig.emailPath || null; + formData.namePath = data.idpOidcConfig.namePath || null; + formData.scopes = data.idpOidcConfig.scopes; + } else if (idpVariant === "azure") { + formData.tenantId = tenantId; + console.log("Setting tenantId in formData:", tenantId); + } + + form.reset(formData); + + // Set the role mapping mode based on the data + // Default to "expression" unless it's a simple roleId or basic '{role name}' pattern + setRoleMappingMode( + isRoleId || isRoleName ? "role" : "expression" + ); + } + } catch (e) { + toast({ + title: t("error"), + description: formatAxiosError(e), + variant: "destructive" + }); + router.push(`/${orgId}/settings/idp`); + } finally { + setInitialLoading(false); + } + }; + + const loadData = async () => { + const rolesRes = await api + .get>(`/org/${orgId}/roles`) + .catch((e) => { + console.error(e); + toast({ + variant: "destructive", + title: t("accessRoleErrorFetch"), + description: formatAxiosError( + e, + t("accessRoleErrorFetchDescription") + ) + }); + return null; + }); + + const availableRoles = + rolesRes?.status === 200 ? rolesRes.data.data.roles : []; + setRoles(availableRoles); + + await loadIdp(availableRoles); + }; + + loadData(); + }, []); + + async function onSubmit(data: GeneralFormValues) { + setLoading(true); + + try { + // Validate against the correct schema based on variant + const schema = getFormSchema(); + const validationResult = schema.safeParse(data); + + if (!validationResult.success) { + // Set form errors + const errors = validationResult.error.flatten().fieldErrors; + Object.keys(errors).forEach((key) => { + const fieldName = key as keyof GeneralFormValues; + const errorMessage = + (errors as any)[key]?.[0] || t("invalidValue"); + form.setError(fieldName, { + type: "manual", + message: errorMessage + }); + }); + setLoading(false); + return; + } + + const roleName = roles.find((r) => r.roleId === data.roleId)?.name; + + // Build payload based on variant + let payload: any = { + name: data.name, + clientId: data.clientId, + clientSecret: data.clientSecret, + autoProvision: data.autoProvision, + roleMapping: + roleMappingMode === "role" + ? `'${roleName}'` + : data.roleMapping || "" + }; + + // Add variant-specific fields + if (variant === "oidc") { + const oidcData = data as OidcFormValues; + payload = { + ...payload, + authUrl: oidcData.authUrl, + tokenUrl: oidcData.tokenUrl, + identifierPath: oidcData.identifierPath, + emailPath: oidcData.emailPath || "", + namePath: oidcData.namePath || "", + scopes: oidcData.scopes + }; + } else if (variant === "azure") { + const azureData = data as AzureFormValues; + // Construct URLs dynamically for Azure provider + const authUrl = `https://login.microsoftonline.com/${azureData.tenantId}/oauth2/v2.0/authorize`; + const tokenUrl = `https://login.microsoftonline.com/${azureData.tenantId}/oauth2/v2.0/token`; + payload = { + ...payload, + authUrl: authUrl, + tokenUrl: tokenUrl, + identifierPath: "email", + emailPath: "email", + namePath: "name", + scopes: "openid profile email" + }; + } else if (variant === "google") { + // Google uses predefined URLs + payload = { + ...payload, + authUrl: "https://accounts.google.com/o/oauth2/v2/auth", + tokenUrl: "https://oauth2.googleapis.com/token", + identifierPath: "email", + emailPath: "email", + namePath: "name", + scopes: "openid profile email" + }; + } + + const res = await api.post( + `/org/${orgId}/idp/${idpId}/oidc`, + payload + ); + + if (res.status === 200) { + toast({ + title: t("success"), + description: t("idpUpdatedDescription") + }); + router.refresh(); + } + } catch (e) { + toast({ + title: t("error"), + description: formatAxiosError(e), + variant: "destructive" + }); + } finally { + setLoading(false); + } + } + + if (initialLoading) { + return null; + } + + return ( + <> + + + + + {t("idpTitle")} + + + {t("idpSettingsDescription")} + + + + + + + {t("redirectUrl")} + + + + + + + + + + + {t("redirectUrlAbout")} + + + {t("redirectUrlAboutDescription")} + + + + {/* IDP Type Indicator */} +
+ + {t("idpTypeLabel")}: + + +
+ +
+ + ( + + + {t("name")} + + + + + + {t("idpDisplayName")} + + + + )} + /> + + +
+
+
+ + {/* Auto Provision Settings */} + + + + {t("idpAutoProvisionUsers")} + + + {t("idpAutoProvisionUsersDescription")} + + + + +
+ + { + form.setValue( + "autoProvision", + checked + ); + }} + roleMappingMode={roleMappingMode} + onRoleMappingModeChange={(data) => { + setRoleMappingMode(data); + // Clear roleId and roleMapping when mode changes + form.setValue("roleId", null); + form.setValue("roleMapping", null); + }} + roles={roles} + roleIdFieldName="roleId" + roleMappingFieldName="roleMapping" + /> + + +
+
+
+ + {/* Google Configuration */} + {variant === "google" && ( + + + + {t("idpGoogleConfiguration")} + + + {t("idpGoogleConfigurationDescription")} + + + + +
+ + ( + + + {t("idpClientId")} + + + + + + {t( + "idpGoogleClientIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientSecret")} + + + + + + {t( + "idpGoogleClientSecretDescription" + )} + + + + )} + /> + + +
+
+
+ )} + + {/* Azure Configuration */} + {variant === "azure" && ( + + + + {t("idpAzureConfiguration")} + + + {t("idpAzureConfigurationDescription")} + + + + +
+ + ( + + + {t("idpTenantId")} + + + + + + {t( + "idpAzureTenantIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientId")} + + + + + + {t( + "idpAzureClientIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientSecret")} + + + + + + {t( + "idpAzureClientSecretDescription" + )} + + + + )} + /> + + +
+
+
+ )} + + {/* OIDC Configuration */} + {variant === "oidc" && ( + + + + + {t("idpOidcConfigure")} + + + {t("idpOidcConfigureDescription")} + + + + +
+ + ( + + + {t("idpClientId")} + + + + + + {t( + "idpClientIdDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpClientSecret" + )} + + + + + + {t( + "idpClientSecretDescription" + )} + + + + )} + /> + + ( + + + {t("idpAuthUrl")} + + + + + + {t( + "idpAuthUrlDescription" + )} + + + + )} + /> + + ( + + + {t("idpTokenUrl")} + + + + + + {t( + "idpTokenUrlDescription" + )} + + + + )} + /> + + +
+
+
+ + + + + {t("idpToken")} + + + {t("idpTokenDescription")} + + + + +
+ + + + + {t("idpJmespathAbout")} + + + {t( + "idpJmespathAboutDescription" + )} + + {t( + "idpJmespathAboutDescriptionLink" + )}{" "} + + + + + + ( + + + {t( + "idpJmespathLabel" + )} + + + + + + {t( + "idpJmespathLabelDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpJmespathEmailPathOptional" + )} + + + + + + {t( + "idpJmespathEmailPathOptionalDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpJmespathNamePathOptional" + )} + + + + + + {t( + "idpJmespathNamePathOptionalDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpOidcConfigureScopes" + )} + + + + + + {t( + "idpOidcConfigureScopesDescription" + )} + + + + )} + /> + + +
+
+
+
+ )} +
+ +
+ +
+ + ); +} diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx new file mode 100644 index 00000000..4846dd56 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx @@ -0,0 +1,63 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { internal } from "@app/lib/api"; +import { GetIdpResponse as GetOrgIdpResponse } from "@server/routers/idp"; +import { AxiosResponse } from "axios"; +import { redirect } from "next/navigation"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { HorizontalTabs } from "@app/components/HorizontalTabs"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from "next-intl/server"; + +interface SettingsLayoutProps { + children: React.ReactNode; + params: Promise<{ orgId: string; idpId: string }>; +} + +export default async function SettingsLayout(props: SettingsLayoutProps) { + const params = await props.params; + const { children } = props; + const t = await getTranslations(); + + let idp = null; + try { + const res = await internal.get>( + `/org/${params.orgId}/idp/${params.idpId}`, + await authCookieHeader() + ); + idp = res.data.data; + } catch { + redirect(`/${params.orgId}/settings/idp`); + } + + const navItems: HorizontalTabs = [ + { + title: t("general"), + href: `/${params.orgId}/settings/idp/${params.idpId}/general` + } + ]; + + return ( + <> + + +
+ {children} +
+ + ); +} diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx new file mode 100644 index 00000000..1d53035c --- /dev/null +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx @@ -0,0 +1,21 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { redirect } from "next/navigation"; + +export default async function IdpPage(props: { + params: Promise<{ orgId: string; idpId: string }>; +}) { + const params = await props.params; + redirect(`/${params.orgId}/settings/idp/${params.idpId}/general`); +} diff --git a/src/app/[orgId]/settings/(private)/idp/create/page.tsx b/src/app/[orgId]/settings/(private)/idp/create/page.tsx new file mode 100644 index 00000000..03f83afe --- /dev/null +++ b/src/app/[orgId]/settings/(private)/idp/create/page.tsx @@ -0,0 +1,870 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { + SettingsContainer, + SettingsSection, + SettingsSectionBody, + SettingsSectionDescription, + SettingsSectionForm, + SettingsSectionGrid, + SettingsSectionHeader, + SettingsSectionTitle +} from "@app/components/Settings"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import HeaderTitle from "@app/components/SettingsSectionTitle"; +import { z } from "zod"; +import { createElement, useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Input } from "@app/components/ui/input"; +import { Button } from "@app/components/ui/button"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { toast } from "@app/hooks/useToast"; +import { useParams, useRouter } from "next/navigation"; +import { Checkbox } from "@app/components/ui/checkbox"; +import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; +import { InfoIcon, ExternalLink } from "lucide-react"; +import { StrategySelect } from "@app/components/StrategySelect"; +import { SwitchInput } from "@app/components/SwitchInput"; +import { Badge } from "@app/components/ui/badge"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import { useTranslations } from "next-intl"; +import Image from "next/image"; +import AutoProvisionConfigWidget from "@app/components/private/AutoProvisionConfigWidget"; +import { AxiosResponse } from "axios"; +import { ListRolesResponse } from "@server/routers/role"; + +export default function Page() { + const { env } = useEnvContext(); + const api = createApiClient({ env }); + const router = useRouter(); + const [createLoading, setCreateLoading] = useState(false); + const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]); + const [roleMappingMode, setRoleMappingMode] = useState< + "role" | "expression" + >("role"); + const { isUnlocked } = useLicenseStatusContext(); + const t = useTranslations(); + + const params = useParams(); + + const createIdpFormSchema = z.object({ + name: z.string().min(2, { message: t("nameMin", { len: 2 }) }), + type: z.enum(["oidc", "google", "azure"]), + clientId: z.string().min(1, { message: t("idpClientIdRequired") }), + clientSecret: z + .string() + .min(1, { message: t("idpClientSecretRequired") }), + authUrl: z + .string() + .url({ message: t("idpErrorAuthUrlInvalid") }) + .optional(), + tokenUrl: z + .string() + .url({ message: t("idpErrorTokenUrlInvalid") }) + .optional(), + identifierPath: z + .string() + .min(1, { message: t("idpPathRequired") }) + .optional(), + emailPath: z.string().optional(), + namePath: z.string().optional(), + scopes: z + .string() + .min(1, { message: t("idpScopeRequired") }) + .optional(), + tenantId: z.string().optional(), + autoProvision: z.boolean().default(false), + roleMapping: z.string().nullable().optional(), + roleId: z.number().nullable().optional() + }); + + type CreateIdpFormValues = z.infer; + + interface ProviderTypeOption { + id: "oidc" | "google" | "azure"; + title: string; + description: string; + icon?: React.ReactNode; + } + + const providerTypes: ReadonlyArray = [ + { + id: "oidc", + title: "OAuth2/OIDC", + description: t("idpOidcDescription") + }, + { + id: "google", + title: t("idpGoogleTitle"), + description: t("idpGoogleDescription"), + icon: ( + {t("idpGoogleAlt")} + ) + }, + { + id: "azure", + title: t("idpAzureTitle"), + description: t("idpAzureDescription"), + icon: ( + {t("idpAzureAlt")} + ) + } + ]; + + const form = useForm({ + resolver: zodResolver(createIdpFormSchema), + defaultValues: { + name: "", + type: "oidc", + clientId: "", + clientSecret: "", + authUrl: "", + tokenUrl: "", + identifierPath: "sub", + namePath: "name", + emailPath: "email", + scopes: "openid profile email", + tenantId: "", + autoProvision: false, + roleMapping: null, + roleId: null + } + }); + + // Fetch roles on component mount + useEffect(() => { + async function fetchRoles() { + const res = await api + .get< + AxiosResponse + >(`/org/${params.orgId}/roles`) + .catch((e) => { + console.error(e); + toast({ + variant: "destructive", + title: t("accessRoleErrorFetch"), + description: formatAxiosError( + e, + t("accessRoleErrorFetchDescription") + ) + }); + }); + + if (res?.status === 200) { + setRoles(res.data.data.roles); + } + } + + fetchRoles(); + }, []); + + // Handle provider type changes and set defaults + const handleProviderChange = (value: "oidc" | "google" | "azure") => { + form.setValue("type", value); + + if (value === "google") { + // Set Google defaults + form.setValue( + "authUrl", + "https://accounts.google.com/o/oauth2/v2/auth" + ); + form.setValue("tokenUrl", "https://oauth2.googleapis.com/token"); + form.setValue("identifierPath", "email"); + form.setValue("emailPath", "email"); + form.setValue("namePath", "name"); + form.setValue("scopes", "openid profile email"); + } else if (value === "azure") { + // Set Azure Entra ID defaults (URLs will be constructed dynamically) + form.setValue( + "authUrl", + "https://login.microsoftonline.com/{{TENANT_ID}}/oauth2/v2.0/authorize" + ); + form.setValue( + "tokenUrl", + "https://login.microsoftonline.com/{{TENANT_ID}}/oauth2/v2.0/token" + ); + form.setValue("identifierPath", "email"); + form.setValue("emailPath", "email"); + form.setValue("namePath", "name"); + form.setValue("scopes", "openid profile email"); + form.setValue("tenantId", ""); + } else { + // Reset to OIDC defaults + form.setValue("authUrl", ""); + form.setValue("tokenUrl", ""); + form.setValue("identifierPath", "sub"); + form.setValue("namePath", "name"); + form.setValue("emailPath", "email"); + form.setValue("scopes", "openid profile email"); + } + }; + + async function onSubmit(data: CreateIdpFormValues) { + setCreateLoading(true); + + try { + // Construct URLs dynamically for Azure provider + let authUrl = data.authUrl; + let tokenUrl = data.tokenUrl; + + if (data.type === "azure" && data.tenantId) { + authUrl = authUrl?.replace("{{TENANT_ID}}", data.tenantId); + tokenUrl = tokenUrl?.replace("{{TENANT_ID}}", data.tenantId); + } + + const roleName = roles.find((r) => r.roleId === data.roleId)?.name; + + const payload = { + name: data.name, + clientId: data.clientId, + clientSecret: data.clientSecret, + authUrl: authUrl, + tokenUrl: tokenUrl, + identifierPath: data.identifierPath, + emailPath: data.emailPath, + namePath: data.namePath, + autoProvision: data.autoProvision, + roleMapping: + roleMappingMode === "role" + ? `'${roleName}'` + : data.roleMapping || "", + scopes: data.scopes, + variant: data.type + }; + + // Use the appropriate endpoint based on provider type + const endpoint = "oidc"; + const res = await api.put( + `/org/${params.orgId}/idp/${endpoint}`, + payload + ); + + if (res.status === 201) { + toast({ + title: t("success"), + description: t("idpCreatedDescription") + }); + router.push( + `/${params.orgId}/settings/idp/${res.data.data.idpId}` + ); + } + } catch (e) { + toast({ + title: t("error"), + description: formatAxiosError(e), + variant: "destructive" + }); + } finally { + setCreateLoading(false); + } + } + + return ( + <> +
+ + +
+ + + + + + {t("idpTitle")} + + + {t("idpCreateSettingsDescription")} + + + + +
+ + ( + + + {t("name")} + + + + + + {t("idpDisplayName")} + + + + )} + /> + + +
+
+
+ + + + + {t("idpType")} + + + {t("idpTypeDescription")} + + + + { + handleProviderChange( + value as "oidc" | "google" | "azure" + ); + }} + cols={3} + /> + + + + {/* Auto Provision Settings */} + + + + {t("idpAutoProvisionUsers")} + + + {t("idpAutoProvisionUsersDescription")} + + + + +
+ + { + form.setValue( + "autoProvision", + checked + ); + }} + roleMappingMode={roleMappingMode} + onRoleMappingModeChange={(data) => { + setRoleMappingMode(data); + // Clear roleId and roleMapping when mode changes + form.setValue("roleId", null); + form.setValue("roleMapping", null); + }} + roles={roles} + roleIdFieldName="roleId" + roleMappingFieldName="roleMapping" + /> + + +
+
+
+ + {form.watch("type") === "google" && ( + + + + {t("idpGoogleConfigurationTitle")} + + + {t("idpGoogleConfigurationDescription")} + + + + +
+ + ( + + + {t("idpClientId")} + + + + + + {t( + "idpGoogleClientIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientSecret")} + + + + + + {t( + "idpGoogleClientSecretDescription" + )} + + + + )} + /> + + +
+
+
+ )} + + {form.watch("type") === "azure" && ( + + + + {t("idpAzureConfigurationTitle")} + + + {t("idpAzureConfigurationDescription")} + + + + +
+ + ( + + + {t("idpTenantIdLabel")} + + + + + + {t( + "idpAzureTenantIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientId")} + + + + + + {t( + "idpAzureClientIdDescription2" + )} + + + + )} + /> + + ( + + + {t("idpClientSecret")} + + + + + + {t( + "idpAzureClientSecretDescription2" + )} + + + + )} + /> + + +
+
+
+ )} + + {form.watch("type") === "oidc" && ( + + + + + {t("idpOidcConfigure")} + + + {t("idpOidcConfigureDescription")} + + + +
+ + ( + + + {t("idpClientId")} + + + + + + {t( + "idpClientIdDescription" + )} + + + + )} + /> + + ( + + + {t("idpClientSecret")} + + + + + + {t( + "idpClientSecretDescription" + )} + + + + )} + /> + + ( + + + {t("idpAuthUrl")} + + + + + + {t( + "idpAuthUrlDescription" + )} + + + + )} + /> + + ( + + + {t("idpTokenUrl")} + + + + + + {t( + "idpTokenUrlDescription" + )} + + + + )} + /> + + + + + + + {t("idpOidcConfigureAlert")} + + + {t("idpOidcConfigureAlertDescription")} + + +
+
+ + + + + {t("idpToken")} + + + {t("idpTokenDescription")} + + + +
+ + + + + {t("idpJmespathAbout")} + + + {t( + "idpJmespathAboutDescription" + )}{" "} + + {t( + "idpJmespathAboutDescriptionLink" + )}{" "} + + + + + + ( + + + {t("idpJmespathLabel")} + + + + + + {t( + "idpJmespathLabelDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpJmespathEmailPathOptional" + )} + + + + + + {t( + "idpJmespathEmailPathOptionalDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpJmespathNamePathOptional" + )} + + + + + + {t( + "idpJmespathNamePathOptionalDescription" + )} + + + + )} + /> + + ( + + + {t( + "idpOidcConfigureScopes" + )} + + + + + + {t( + "idpOidcConfigureScopesDescription" + )} + + + + )} + /> + + +
+
+
+ )} +
+ +
+ + +
+ + ); +} diff --git a/src/app/[orgId]/settings/(private)/idp/page.tsx b/src/app/[orgId]/settings/(private)/idp/page.tsx new file mode 100644 index 00000000..1032e66c --- /dev/null +++ b/src/app/[orgId]/settings/(private)/idp/page.tsx @@ -0,0 +1,81 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { internal, priv } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { AxiosResponse } from "axios"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import IdpTable, { IdpRow } from "@app/components/private/OrgIdpTable"; +import { getTranslations } from "next-intl/server"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; +import { cache } from "react"; +import { + GetOrgSubscriptionResponse, + GetOrgTierResponse +} from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; +import { build } from "@server/build"; + +type OrgIdpPageProps = { + params: Promise<{ orgId: string }>; +}; + +export const dynamic = "force-dynamic"; + +export default async function OrgIdpPage(props: OrgIdpPageProps) { + const params = await props.params; + + let idps: IdpRow[] = []; + try { + const res = await internal.get>( + `/org/${params.orgId}/idp`, + await authCookieHeader() + ); + idps = res.data.data.idps; + } catch (e) { + console.error(e); + } + + const t = await getTranslations(); + + let subscriptionStatus: GetOrgTierResponse | null = null; + try { + const getSubscription = cache(() => + priv.get>( + `/org/${params.orgId}/billing/tier` + ) + ); + const subRes = await getSubscription(); + subscriptionStatus = subRes.data.data; + } catch {} + const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + + return ( + <> + + + {build === "saas" && !subscribed ? ( + + + {t("idpDisabled")} {t("subscriptionRequiredToUse")} + + + ) : null} + + + + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx new file mode 100644 index 00000000..4a1db4ea --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx @@ -0,0 +1,55 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from "next-intl"; + +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; + createRemoteExitNode?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; +} + +export function ExitNodesDataTable({ + columns, + data, + createRemoteExitNode, + onRefresh, + isRefreshing +}: DataTableProps) { + + const t = useTranslations(); + + return ( + + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx new file mode 100644 index 00000000..11e0bcfc --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx @@ -0,0 +1,319 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { ExitNodesDataTable } from "./ExitNodesDataTable"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from "@app/components/ui/dropdown-menu"; +import { Button } from "@app/components/ui/button"; +import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useState, useEffect } from "react"; +import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import { toast } from "@app/hooks/useToast"; +import { formatAxiosError } from "@app/lib/api"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; +import { Badge } from "@app/components/ui/badge"; + +export type RemoteExitNodeRow = { + id: string; + exitNodeId: number | null; + name: string; + address: string; + endpoint: string; + orgId: string; + type: string | null; + online: boolean; + dateCreated: string; + version?: string; +}; + +type ExitNodesTableProps = { + remoteExitNodes: RemoteExitNodeRow[]; + orgId: string; +}; + +export default function ExitNodesTable({ + remoteExitNodes, + orgId +}: ExitNodesTableProps) { + const router = useRouter(); + + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedNode, setSelectedNode] = useState( + null + ); + const [rows, setRows] = useState(remoteExitNodes); + const [isRefreshing, setIsRefreshing] = useState(false); + + const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + useEffect(() => { + setRows(remoteExitNodes); + }, [remoteExitNodes]); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + + const deleteRemoteExitNode = (remoteExitNodeId: string) => { + api.delete(`/org/${orgId}/remote-exit-node/${remoteExitNodeId}`) + .catch((e) => { + console.error(t("remoteExitNodeErrorDelete"), e); + toast({ + variant: "destructive", + title: t("remoteExitNodeErrorDelete"), + description: formatAxiosError( + e, + t("remoteExitNodeErrorDelete") + ) + }); + }) + .then(() => { + setIsDeleteModalOpen(false); + + const newRows = rows.filter( + (row) => row.id !== remoteExitNodeId + ); + setRows(newRows); + }); + }; + + const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => { + return ( + + ); + } + }, + { + accessorKey: "online", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const originalRow = row.original; + if (originalRow.online) { + return ( + +
+ {t("online")} +
+ ); + } else { + return ( + +
+ {t("offline")} +
+ ); + } + } + }, + { + accessorKey: "type", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const originalRow = row.original; + return ( + + {originalRow.type === "remoteExitNode" + ? "Remote Exit Node" + : originalRow.type} + + ); + } + }, + { + accessorKey: "address", + header: ({ column }) => { + return ( + + ); + } + }, + { + accessorKey: "endpoint", + header: ({ column }) => { + return ( + + ); + } + }, + { + accessorKey: "version", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const originalRow = row.original; + return originalRow.version || "-"; + } + }, + { + id: "actions", + cell: ({ row }) => { + const nodeRow = row.original; + return ( +
+ + + + + + { + setSelectedNode(nodeRow); + setIsDeleteModalOpen(true); + }} + > + + {t("delete")} + + + + +
+ ); + } + } + ]; + + return ( + <> + {selectedNode && ( + { + setIsDeleteModalOpen(val); + setSelectedNode(null); + }} + dialog={ +
+

+ {t("remoteExitNodeQuestionRemove", { + selectedNode: + selectedNode?.name || selectedNode?.id + })} +

+ +

{t("remoteExitNodeMessageRemove")}

+ +

{t("remoteExitNodeMessageConfirm")}

+
+ } + buttonText={t("remoteExitNodeConfirmDelete")} + onConfirm={async () => + deleteRemoteExitNode(selectedNode!.id) + } + string={selectedNode.name} + title={t("remoteExitNodeDelete")} + /> + )} + + + router.push(`/${orgId}/settings/remote-exit-nodes/create`) + } + onRefresh={refreshData} + isRefreshing={isRefreshing} + /> + + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx new file mode 100644 index 00000000..b5835e1b --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx @@ -0,0 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export default function GeneralPage() { + return <>; +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx new file mode 100644 index 00000000..653444e8 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx @@ -0,0 +1,59 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { internal } from "@app/lib/api"; +import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import { AxiosResponse } from "axios"; +import { redirect } from "next/navigation"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from "next-intl/server"; +import RemoteExitNodeProvider from "@app/providers/PrivateRemoteExitNodeProvider"; + +interface SettingsLayoutProps { + children: React.ReactNode; + params: Promise<{ remoteExitNodeId: string; orgId: string }>; +} + +export default async function SettingsLayout(props: SettingsLayoutProps) { + const params = await props.params; + const { children } = props; + + let remoteExitNode = null; + try { + const res = await internal.get< + AxiosResponse + >( + `/org/${params.orgId}/remote-exit-node/${params.remoteExitNodeId}`, + await authCookieHeader() + ); + remoteExitNode = res.data.data; + } catch { + redirect(`/${params.orgId}/settings/remote-exit-nodes`); + } + + const t = await getTranslations(); + + return ( + <> + + + +
{children}
+
+ + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx new file mode 100644 index 00000000..badf0971 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx @@ -0,0 +1,23 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { redirect } from "next/navigation"; + +export default async function RemoteExitNodePage(props: { + params: Promise<{ orgId: string; remoteExitNodeId: string }>; +}) { + const params = await props.params; + redirect( + `/${params.orgId}/settings/remote-exit-nodes/${params.remoteExitNodeId}/general` + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx new file mode 100644 index 00000000..5daaa493 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx @@ -0,0 +1,379 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { + SettingsContainer, + SettingsSection, + SettingsSectionBody, + SettingsSectionDescription, + SettingsSectionForm, + SettingsSectionHeader, + SettingsSectionTitle +} from "@app/components/Settings"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import { z } from "zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Input } from "@app/components/ui/input"; +import { Button } from "@app/components/ui/button"; +import CopyTextBox from "@app/components/CopyTextBox"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { + QuickStartRemoteExitNodeResponse, + PickRemoteExitNodeDefaultsResponse +} from "@server/routers/private/remoteExitNode"; +import { toast } from "@app/hooks/useToast"; +import { AxiosResponse } from "axios"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; +import { useTranslations } from "next-intl"; +import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; +import { InfoIcon } from "lucide-react"; +import HeaderTitle from "@app/components/SettingsSectionTitle"; +import { StrategySelect } from "@app/components/StrategySelect"; + +export default function CreateRemoteExitNodePage() { + const { env } = useEnvContext(); + const api = createApiClient({ env }); + const { orgId } = useParams(); + const router = useRouter(); + const searchParams = useSearchParams(); + const t = useTranslations(); + + const [isLoading, setIsLoading] = useState(false); + const [defaults, setDefaults] = + useState(null); + const [createdNode, setCreatedNode] = + useState(null); + const [strategy, setStrategy] = useState<"adopt" | "generate">("adopt"); + + const createRemoteExitNodeFormSchema = z + .object({ + remoteExitNodeId: z.string().optional(), + secret: z.string().optional() + }) + .refine( + (data) => { + if (strategy === "adopt") { + return data.remoteExitNodeId && data.secret; + } + return true; + }, + { + message: t("remoteExitNodeCreate.validation.adoptRequired"), + path: ["remoteExitNodeId"] + } + ); + + type CreateRemoteExitNodeFormValues = z.infer< + typeof createRemoteExitNodeFormSchema + >; + + const form = useForm({ + resolver: zodResolver(createRemoteExitNodeFormSchema), + defaultValues: {} + }); + + // Check for query parameters and prefill form + useEffect(() => { + const remoteExitNodeId = searchParams.get("remoteExitNodeId"); + const remoteExitNodeSecret = searchParams.get("remoteExitNodeSecret"); + + if (remoteExitNodeId && remoteExitNodeSecret) { + setStrategy("adopt"); + form.setValue("remoteExitNodeId", remoteExitNodeId); + form.setValue("secret", remoteExitNodeSecret); + } + }, []); + + useEffect(() => { + const loadDefaults = async () => { + try { + const response = await api.get< + AxiosResponse + >(`/org/${orgId}/pick-remote-exit-node-defaults`); + setDefaults(response.data.data); + } catch (error) { + toast({ + title: t("error"), + description: t( + "remoteExitNodeCreate.errors.loadDefaultsFailed" + ), + variant: "destructive" + }); + } + }; + + // Only load defaults when strategy is "generate" + if (strategy === "generate") { + loadDefaults(); + } + }, [strategy]); + + const onSubmit = async (data: CreateRemoteExitNodeFormValues) => { + if (strategy === "generate" && !defaults) { + toast({ + title: t("error"), + description: t("remoteExitNodeCreate.errors.defaultsNotLoaded"), + variant: "destructive" + }); + return; + } + + if (strategy === "adopt" && (!data.remoteExitNodeId || !data.secret)) { + toast({ + title: t("error"), + description: t("remoteExitNodeCreate.validation.adoptRequired"), + variant: "destructive" + }); + return; + } + + setIsLoading(true); + try { + const response = await api.put< + AxiosResponse + >(`/org/${orgId}/remote-exit-node`, { + remoteExitNodeId: + strategy === "generate" + ? defaults!.remoteExitNodeId + : data.remoteExitNodeId!, + secret: + strategy === "generate" ? defaults!.secret : data.secret! + }); + setCreatedNode(response.data.data); + + router.push(`/${orgId}/settings/remote-exit-nodes`); + } catch (error) { + toast({ + title: t("error"), + description: formatAxiosError( + error, + t("remoteExitNodeCreate.errors.createFailed") + ), + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + return ( + <> +
+ + +
+ +
+ + + + + {t("remoteExitNodeCreate.strategy.title")} + + + {t("remoteExitNodeCreate.strategy.description")} + + + + { + setStrategy(value); + // Clear adopt fields when switching to generate + if (value === "generate") { + form.setValue("remoteExitNodeId", ""); + form.setValue("secret", ""); + } + }} + cols={2} + /> + + + + {strategy === "adopt" && ( + + + + {t("remoteExitNodeCreate.adopt.title")} + + + {t( + "remoteExitNodeCreate.adopt.description" + )} + + + + +
+
+ ( + + + {t( + "remoteExitNodeCreate.adopt.nodeIdLabel" + )} + + + + + + {t( + "remoteExitNodeCreate.adopt.nodeIdDescription" + )} + + + + )} + /> + + ( + + + {t( + "remoteExitNodeCreate.adopt.secretLabel" + )} + + + + + + {t( + "remoteExitNodeCreate.adopt.secretDescription" + )} + + + + )} + /> +
+
+
+
+
+ )} + + {strategy === "generate" && ( + + + + {t("remoteExitNodeCreate.generate.title")} + + + {t( + "remoteExitNodeCreate.generate.description" + )} + + + + + + + + {t( + "remoteExitNodeCreate.generate.saveCredentialsTitle" + )} + + + {t( + "remoteExitNodeCreate.generate.saveCredentialsDescription" + )} + + + + + )} +
+ +
+ + +
+
+ + ); +} diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx new file mode 100644 index 00000000..b18df692 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx @@ -0,0 +1,72 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { internal } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; +import { AxiosResponse } from "axios"; +import ExitNodesTable, { RemoteExitNodeRow } from "./ExitNodesTable"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from "next-intl/server"; + +type RemoteExitNodesPageProps = { + params: Promise<{ orgId: string }>; +}; + +export const dynamic = "force-dynamic"; + +export default async function RemoteExitNodesPage( + props: RemoteExitNodesPageProps +) { + const params = await props.params; + let remoteExitNodes: ListRemoteExitNodesResponse["remoteExitNodes"] = []; + try { + const res = await internal.get< + AxiosResponse + >(`/org/${params.orgId}/remote-exit-nodes`, await authCookieHeader()); + remoteExitNodes = res.data.data.remoteExitNodes; + } catch (e) {} + + const t = await getTranslations(); + + const remoteExitNodeRows: RemoteExitNodeRow[] = remoteExitNodes.map( + (node) => { + return { + name: node.name, + id: node.remoteExitNodeId, + exitNodeId: node.exitNodeId, + address: node.address?.split("/")[0] || "-", + endpoint: node.endpoint || "-", + online: node.online, + type: node.type, + dateCreated: node.dateCreated, + version: node.version || undefined, + orgId: params.orgId + }; + } + ); + + return ( + <> + + + + + ); +} diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 2df8413f..3e6d2458 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -47,6 +47,8 @@ import { ListIdpsResponse } from "@server/routers/idp"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; import Image from "next/image"; +import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; +import { TierId } from "@server/lib/private/billing/tiers"; type UserType = "internal" | "oidc"; @@ -74,6 +76,9 @@ export default function Page() { const api = createApiClient({ env }); const t = useTranslations(); + const subscription = usePrivateSubscriptionStatusContext(); + const subscribed = subscription?.getTier() === TierId.STANDARD; + const [selectedOption, setSelectedOption] = useState("internal"); const [inviteLink, setInviteLink] = useState(null); const [loading, setLoading] = useState(false); @@ -227,8 +232,14 @@ export default function Page() { } async function fetchIdps() { + if (build === "saas" && !subscribed) { + return; + } + const res = await api - .get>("/idp") + .get< + AxiosResponse + >(build === "saas" ? `/org/${orgId}/idp` : "/idp") .catch((e) => { console.error(e); toast({ @@ -430,7 +441,7 @@ export default function Page() {
- {!inviteLink && build !== "saas" && dataLoaded ? ( + {!inviteLink ? ( diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index c4bb3ccc..a7948536 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -1,10 +1,12 @@ "use client"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import AuthPageSettings, { AuthPageSettingsRef } from "@app/components/private/AuthPageSettings"; + import { Button } from "@app/components/ui/button"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { userOrgUserContext } from "@app/hooks/useOrgUserContext"; import { toast } from "@app/hooks/useToast"; -import { useState } from "react"; +import { useState, useRef } from "react"; import { Form, FormControl, @@ -15,6 +17,7 @@ import { FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; + import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -38,7 +41,7 @@ import { useUserContext } from "@app/hooks/useUserContext"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; -// Updated schema to include subnet field +// Schema for general organization settings const GeneralFormSchema = z.object({ name: z.string(), subnet: z.string().optional() @@ -58,6 +61,7 @@ export default function GeneralPage() { const [loadingDelete, setLoadingDelete] = useState(false); const [loadingSave, setLoadingSave] = useState(false); + const authPageSettingsRef = useRef(null); const form = useForm({ resolver: zodResolver(GeneralFormSchema), @@ -121,28 +125,33 @@ export default function GeneralPage() { async function onSubmit(data: GeneralFormValues) { setLoadingSave(true); - await api - .post(`/org/${org?.org.orgId}`, { - name: data.name, + + try { + // Update organization + await api.post(`/org/${org?.org.orgId}`, { + name: data.name // subnet: data.subnet // Include subnet in the API request - }) - .then(() => { - toast({ - title: t("orgUpdated"), - description: t("orgUpdatedDescription") - }); - router.refresh(); - }) - .catch((e) => { - toast({ - variant: "destructive", - title: t("orgErrorUpdate"), - description: formatAxiosError(e, t("orgErrorUpdateMessage")) - }); - }) - .finally(() => { - setLoadingSave(false); }); + + // Also save auth page settings if they have unsaved changes + if (build === "saas" && authPageSettingsRef.current?.hasUnsavedChanges()) { + await authPageSettingsRef.current.saveAuthSettings(); + } + + toast({ + title: t("orgUpdated"), + description: t("orgUpdatedDescription") + }); + router.refresh(); + } catch (e) { + toast({ + variant: "destructive", + title: t("orgErrorUpdate"), + description: formatAxiosError(e, t("orgErrorUpdateMessage")) + }); + } finally { + setLoadingSave(false); + } } return ( @@ -207,7 +216,9 @@ export default function GeneralPage() { name="subnet" render={({ field }) => ( - Subnet + + {t("subnet")} + - The subnet for this - organization's network - configuration. + {t("subnetDescription")} )} @@ -228,18 +237,23 @@ export default function GeneralPage() { - - - - {build === "oss" && ( + + {build === "saas" && } + + {/* Save Button */} +
+ +
+ + {build !== "saas" && ( @@ -262,6 +276,7 @@ export default function GeneralPage() { )} +
); } diff --git a/src/app/[orgId]/settings/layout.tsx b/src/app/[orgId]/settings/layout.tsx index 7db530dd..d35af6e6 100644 --- a/src/app/[orgId]/settings/layout.tsx +++ b/src/app/[orgId]/settings/layout.tsx @@ -27,7 +27,7 @@ import { orgNavSections } from "@app/app/navigation"; export const dynamic = "force-dynamic"; export const metadata: Metadata = { - title: `Settings - Pangolin`, + title: `Settings - ${process.env.BRANDING_APP_NAME || "Pangolin"}`, description: "" }; diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index d53cb0c0..ae8e52ab 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -58,6 +58,9 @@ import { SelectValue } from "@app/components/ui/select"; import { Separator } from "@app/components/ui/separator"; +import { build } from "@server/build"; +import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; +import { TierId } from "@server/lib/private/billing/tiers"; const UsersRolesFormSchema = z.object({ roles: z.array( @@ -94,6 +97,9 @@ export default function ResourceAuthenticationPage() { const router = useRouter(); const t = useTranslations(); + const subscription = usePrivateSubscriptionStatusContext(); + const subscribed = subscription?.getTier() === TierId.STANDARD; + const [pageLoading, setPageLoading] = useState(true); const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>( @@ -178,7 +184,7 @@ export default function ResourceAuthenticationPage() { AxiosResponse<{ idps: { idpId: number; name: string }[]; }> - >("/idp") + >(build === "saas" ? `/org/${org?.org.orgId}/idp` : "/idp") ]); setAllRoles( @@ -223,12 +229,23 @@ export default function ResourceAuthenticationPage() { })) ); - setAllIdps( - idpsResponse.data.data.idps.map((idp) => ({ - id: idp.idpId, - text: idp.name - })) - ); + if (build === "saas") { + if (subscribed) { + setAllIdps( + idpsResponse.data.data.idps.map((idp) => ({ + id: idp.idpId, + text: idp.name + })) + ); + } + } else { + setAllIdps( + idpsResponse.data.data.idps.map((idp) => ({ + id: idp.idpId, + text: idp.name + })) + ); + } if ( autoLoginEnabled && diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index b37cfba9..a4277f6b 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -79,6 +79,7 @@ import { import { ContainersSelector } from "@app/components/ContainersSelector"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; +import HealthCheckDialog from "@/components/HealthCheckDialog"; import { DockerManager, DockerState } from "@app/lib/docker"; import { Container } from "@server/routers/site"; import { @@ -98,50 +99,64 @@ import { } from "@app/components/ui/command"; import { parseHostTarget } from "@app/lib/parseHostTarget"; import { HeadersInput } from "@app/components/HeadersInput"; -import { PathMatchDisplay, PathMatchModal, PathRewriteDisplay, PathRewriteModal } from "@app/components/PathMatchRenameModal"; +import { + PathMatchDisplay, + PathMatchModal, + PathRewriteDisplay, + PathRewriteModal +} from "@app/components/PathMatchRenameModal"; +import { Badge } from "@app/components/ui/badge"; -const addTargetSchema = z.object({ - ip: z.string().refine(isTargetValid), - method: z.string().nullable(), - port: z.coerce.number().int().positive(), - siteId: z.number().int().positive(), - path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), - rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() -}).refine( - (data) => { - // If path is provided, pathMatchType must be provided - if (data.path && !data.pathMatchType) { - return false; - } - // If pathMatchType is provided, path must be provided - if (data.pathMatchType && !data.path) { - return false; - } - // Validate path based on pathMatchType - if (data.path && data.pathMatchType) { - switch (data.pathMatchType) { - case "exact": - case "prefix": - // Path should start with / - return data.path.startsWith("/"); - case "regex": - // Validate regex - try { - new RegExp(data.path); - return true; - } catch { - return false; - } +const addTargetSchema = z + .object({ + ip: z.string().refine(isTargetValid), + method: z.string().nullable(), + port: z.coerce.number().int().positive(), + siteId: z.number().int().positive(), + path: z.string().optional().nullable(), + pathMatchType: z + .enum(["exact", "prefix", "regex"]) + .optional() + .nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z + .enum(["exact", "prefix", "regex", "stripPrefix"]) + .optional() + .nullable() + }) + .refine( + (data) => { + // If path is provided, pathMatchType must be provided + if (data.path && !data.pathMatchType) { + return false; } + // If pathMatchType is provided, path must be provided + if (data.pathMatchType && !data.path) { + return false; + } + // Validate path based on pathMatchType + if (data.path && data.pathMatchType) { + switch (data.pathMatchType) { + case "exact": + case "prefix": + // Path should start with / + return data.path.startsWith("/"); + case "regex": + // Validate regex + try { + new RegExp(data.path); + return true; + } catch { + return false; + } + } + } + return true; + }, + { + message: "Invalid path configuration" } - return true; - }, - { - message: "Invalid path configuration" - } -) + ) .refine( (data) => { // If rewritePath is provided, rewritePathType must be provided @@ -229,6 +244,10 @@ export default function ReverseProxyTargets(props: { const [proxySettingsLoading, setProxySettingsLoading] = useState(false); const [pageLoading, setPageLoading] = useState(true); + const [isAdvancedOpen, setIsAdvancedOpen] = useState(false); + const [healthCheckDialogOpen, setHealthCheckDialogOpen] = useState(false); + const [selectedTargetForHealthCheck, setSelectedTargetForHealthCheck] = + useState(null); const router = useRouter(); const proxySettingsSchema = z.object({ @@ -246,7 +265,9 @@ export default function ReverseProxyTargets(props: { message: t("proxyErrorInvalidHeader") } ), - headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable() + headers: z + .array(z.object({ name: z.string(), value: z.string() })) + .nullable() }); const tlsSettingsSchema = z.object({ @@ -280,7 +301,7 @@ export default function ReverseProxyTargets(props: { path: null, pathMatchType: null, rewritePath: null, - rewritePathType: null, + rewritePathType: null } as z.infer }); @@ -463,7 +484,21 @@ export default function ReverseProxyTargets(props: { enabled: true, targetId: new Date().getTime(), new: true, - resourceId: resource.resourceId + resourceId: resource.resourceId, + hcEnabled: false, + hcPath: null, + hcMethod: null, + hcInterval: null, + hcTimeout: null, + hcHeaders: null, + hcScheme: null, + hcHostname: null, + hcPort: null, + hcFollowRedirects: null, + hcHealth: "unknown", + hcStatus: null, + hcMode: null, + hcUnhealthyInterval: null }; setTargets([...targets, newTarget]); @@ -474,7 +509,7 @@ export default function ReverseProxyTargets(props: { path: null, pathMatchType: null, rewritePath: null, - rewritePathType: null, + rewritePathType: null }); } @@ -494,16 +529,36 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...data, - updated: true, - siteType: site?.type || null - } + ...target, + ...data, + updated: true, + siteType: site?.type || null + } : target ) ); } + function updateTargetHealthCheck(targetId: number, config: any) { + setTargets( + targets.map((target) => + target.targetId === targetId + ? { + ...target, + ...config, + updated: true + } + : target + ) + ); + } + + const openHealthCheckDialog = (target: LocalTarget) => { + console.log(target); + setSelectedTargetForHealthCheck(target); + setHealthCheckDialogOpen(true); + }; + async function saveAllSettings() { try { setTargetsLoading(true); @@ -518,6 +573,17 @@ export default function ReverseProxyTargets(props: { method: target.method, enabled: target.enabled, siteId: target.siteId, + hcEnabled: target.hcEnabled, + hcPath: target.hcPath || null, + hcScheme: target.hcScheme || null, + hcHostname: target.hcHostname || null, + hcPort: target.hcPort || null, + hcInterval: target.hcInterval || null, + hcTimeout: target.hcTimeout || null, + hcHeaders: target.hcHeaders || null, + hcFollowRedirects: target.hcFollowRedirects || null, + hcMethod: target.hcMethod || null, + hcStatus: target.hcStatus || null, path: target.path, pathMatchType: target.pathMatchType, rewritePath: target.rewritePath, @@ -598,16 +664,20 @@ export default function ReverseProxyTargets(props: { accessorKey: "path", header: t("matchPath"), cell: ({ row }) => { - const hasPathMatch = !!(row.original.path || row.original.pathMatchType); + const hasPathMatch = !!( + row.original.path || row.original.pathMatchType + ); return hasPathMatch ? (
updateTarget(row.original.targetId, config)} + onChange={(config) => + updateTarget(row.original.targetId, config) + } trigger={ @@ -646,9 +717,11 @@ export default function ReverseProxyTargets(props: { updateTarget(row.original.targetId, config)} + onChange={(config) => + updateTarget(row.original.targetId, config) + } trigger={ @@ -896,7 +976,7 @@ export default function ReverseProxyTargets(props: { updateTarget(row.original.targetId, { ...row.original, rewritePath: null, - rewritePathType: null, + rewritePathType: null }); }} > @@ -907,9 +987,11 @@ export default function ReverseProxyTargets(props: { updateTarget(row.original.targetId, config)} + onChange={(config) => + updateTarget(row.original.targetId, config) + } trigger={ +
+ ) : ( + + {t("healthCheckNotAvailable")} + + )} + + ); + } + }, { accessorKey: "enabled", header: t("enabled"), @@ -1034,21 +1189,21 @@ export default function ReverseProxyTargets(props: { className={cn( "justify-between flex-1", !field.value && - "text-muted-foreground" + "text-muted-foreground" )} > {field.value ? sites.find( - ( - site - ) => - site.siteId === - field.value - ) - ?.name + ( + site + ) => + site.siteId === + field.value + ) + ?.name : t( - "siteSelect" - )} + "siteSelect" + )} @@ -1114,34 +1269,34 @@ export default function ReverseProxyTargets(props: { ); return selectedSite && selectedSite.type === - "newt" + "newt" ? (() => { - const dockerState = - getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })() + const dockerState = + getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })() : null; })()}
@@ -1369,12 +1524,12 @@ export default function ReverseProxyTargets(props: { {header.isPlaceholder ? null : flexRender( - header - .column - .columnDef - .header, - header.getContext() - )} + header + .column + .columnDef + .header, + header.getContext() + )} ) )} @@ -1544,9 +1699,7 @@ export default function ReverseProxyTargets(props: { { field.onChange( value @@ -1588,6 +1741,56 @@ export default function ReverseProxyTargets(props: { {t("saveSettings")}
+ + {selectedTargetForHealthCheck && ( + { + if (selectedTargetForHealthCheck) { + console.log(config); + updateTargetHealthCheck( + selectedTargetForHealthCheck.targetId, + config + ); + } + }} + /> + )} ); } diff --git a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx index 284573b2..8d5ad7d3 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx @@ -58,7 +58,7 @@ import { import { ListResourceRulesResponse } from "@server/routers/resource/listResourceRules"; import { SwitchInput } from "@app/components/SwitchInput"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; -import { ArrowUpDown, Check, InfoIcon, X } from "lucide-react"; +import { ArrowUpDown, Check, InfoIcon, X, ChevronsUpDown } from "lucide-react"; import { InfoSection, InfoSections, @@ -73,6 +73,20 @@ import { import { Switch } from "@app/components/ui/switch"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; +import { COUNTRIES } from "@server/db/countries"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from "@app/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger +} from "@app/components/ui/popover"; // Schema for rule validation const addRuleSchema = z.object({ @@ -98,9 +112,13 @@ export default function ResourceRules(props: { const [loading, setLoading] = useState(false); const [pageLoading, setPageLoading] = useState(true); const [rulesEnabled, setRulesEnabled] = useState(resource.applyRules); + const [openCountrySelect, setOpenCountrySelect] = useState(false); + const [countrySelectValue, setCountrySelectValue] = useState(""); + const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] = useState(false); const router = useRouter(); const t = useTranslations(); - + const env = useEnvContext(); + const isMaxmindAvailable = env.env.server.maxmind_db_path && env.env.server.maxmind_db_path.length > 0; const RuleAction = { ACCEPT: t('alwaysAllow'), @@ -111,7 +129,8 @@ export default function ResourceRules(props: { const RuleMatch = { PATH: t('path'), IP: "IP", - CIDR: t('ipAddressRange') + CIDR: t('ipAddressRange'), + GEOIP: t('country') } as const; const addRuleForm = useForm({ @@ -193,6 +212,15 @@ export default function ResourceRules(props: { setLoading(false); return; } + if (data.match === "GEOIP" && !COUNTRIES.some(c => c.code === data.value)) { + toast({ + variant: "destructive", + title: t('rulesErrorInvalidCountry'), + description: t('rulesErrorInvalidCountryDescription') || "Invalid country code." + }); + setLoading(false); + return; + } // find the highest priority and add one let priority = data.priority; @@ -242,6 +270,8 @@ export default function ResourceRules(props: { return t('rulesMatchIpAddress'); case "PATH": return t('rulesMatchUrl'); + case "GEOIP": + return t('rulesMatchCountry'); } } @@ -461,8 +491,8 @@ export default function ResourceRules(props: { cell: ({ row }) => ( ) @@ -480,15 +513,61 @@ export default function ResourceRules(props: { accessorKey: "value", header: t('value'), cell: ({ row }) => ( - - updateRule(row.original.ruleId, { - value: e.target.value - }) - } - /> + row.original.match === "GEOIP" ? ( + + + + + + + + + {t('noCountryFound')} + + {COUNTRIES.map((country) => ( + { + updateRule(row.original.ruleId, { value: country.code }); + }} + > + + {country.name} ({country.code}) + + ))} + + + + + + ) : ( + + updateRule(row.original.ruleId, { + value: e.target.value + }) + } + /> + ) ) }, { @@ -650,9 +729,7 @@ export default function ResourceRules(props: { @@ -692,7 +774,55 @@ export default function ResourceRules(props: { } /> - + {addRuleForm.watch("match") === "GEOIP" ? ( + + + + + + + + + {t('noCountryFound')} + + {COUNTRIES.map((country) => ( + { + field.onChange(country.code); + setOpenAddRuleCountrySelect(false); + }} + > + + {country.name} ({country.code}) + + ))} + + + + + + ) : ( + + )}
diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index bb19cc79..1810f09e 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -340,7 +340,21 @@ export default function Page() { enabled: true, targetId: new Date().getTime(), new: true, - resourceId: 0 // Will be set when resource is created + resourceId: 0, // Will be set when resource is created + hcEnabled: false, + hcPath: null, + hcMethod: null, + hcInterval: null, + hcTimeout: null, + hcHeaders: null, + hcScheme: null, + hcHostname: null, + hcPort: null, + hcFollowRedirects: null, + hcHealth: "unknown", + hcStatus: null, + hcMode: null, + hcUnhealthyInterval: null }; setTargets([...targets, newTarget]); @@ -446,6 +460,18 @@ export default function Page() { method: target.method, enabled: target.enabled, siteId: target.siteId, + hcEnabled: target.hcEnabled, + hcPath: target.hcPath || null, + hcMethod: target.hcMethod || null, + hcInterval: target.hcInterval || null, + hcTimeout: target.hcTimeout || null, + hcHeaders: target.hcHeaders || null, + hcScheme: target.hcScheme || null, + hcHostname: target.hcHostname || null, + hcPort: target.hcPort || null, + hcFollowRedirects: + target.hcFollowRedirects || null, + hcStatus: target.hcStatus || null, path: target.path, pathMatchType: target.pathMatchType, rewritePath: target.rewritePath, diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index c2990c78..78fbfc0d 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -42,10 +42,7 @@ import { FaFreebsd, FaWindows } from "react-icons/fa"; -import { - SiNixos, - SiKubernetes -} from "react-icons/si"; +import { SiNixos, SiKubernetes } from "react-icons/si"; import { Checkbox, CheckboxWithLabel } from "@app/components/ui/checkbox"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { generateKeypair } from "../[niceId]/wireguardConfig"; @@ -56,6 +53,7 @@ import { CreateSiteResponse, PickSiteDefaultsResponse } from "@server/routers/site"; +import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter } from "next/navigation"; @@ -73,6 +71,13 @@ interface TunnelTypeOption { disabled?: boolean; } +interface RemoteExitNodeOption { + id: string; + title: string; + description: string; + disabled?: boolean; +} + type Commands = { mac: Record; linux: Record; @@ -115,7 +120,8 @@ export default function Page() { method: z.enum(["newt", "wireguard", "local"]), copied: z.boolean(), clientAddress: z.string().optional(), - acceptClients: z.boolean() + acceptClients: z.boolean(), + exitNodeId: z.number().optional() }) .refine( (data) => { @@ -123,12 +129,25 @@ export default function Page() { // return data.copied; return true; } - return true; + // For local sites, require exitNodeId + return build == "saas" ? data.exitNodeId !== undefined : true; }, { message: t("sitesConfirmCopy"), path: ["copied"] } + ) + .refine( + (data) => { + if (data.method === "local" && build == "saas") { + return data.exitNodeId !== undefined; + } + return true; + }, + { + message: t("remoteExitNodeRequired"), + path: ["exitNodeId"] + } ); type CreateSiteFormValues = z.infer; @@ -148,7 +167,10 @@ export default function Page() { { id: "wireguard" as SiteType, title: t("siteWg"), - description: build == "saas" ? t("siteWgDescriptionSaas") : t("siteWgDescription"), + description: + build == "saas" + ? t("siteWgDescriptionSaas") + : t("siteWgDescription"), disabled: true } ]), @@ -158,7 +180,10 @@ export default function Page() { { id: "local" as SiteType, title: t("local"), - description: build == "saas" ? t("siteLocalDescriptionSaas") : t("siteLocalDescription") + description: + build == "saas" + ? t("siteLocalDescriptionSaas") + : t("siteLocalDescription") } ]) ]); @@ -184,6 +209,13 @@ export default function Page() { const [siteDefaults, setSiteDefaults] = useState(null); + const [remoteExitNodeOptions, setRemoteExitNodeOptions] = useState< + ReadonlyArray + >([]); + const [selectedExitNodeId, setSelectedExitNodeId] = useState< + string | undefined + >(); + const hydrateWireGuardConfig = ( privateKey: string, publicKey: string, @@ -320,7 +352,7 @@ WantedBy=default.target` nixos: { All: [ `nix run 'nixpkgs#fosrl-newt' -- --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` - ], + ] // aarch64: [ // `nix run 'nixpkgs#fosrl-newt' -- --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` // ] @@ -432,7 +464,8 @@ WantedBy=default.target` copied: false, method: "newt", clientAddress: "", - acceptClients: false + acceptClients: false, + exitNodeId: undefined } }); @@ -482,6 +515,22 @@ WantedBy=default.target` address: clientAddress }; } + if (data.method === "local" && build == "saas") { + if (!data.exitNodeId) { + toast({ + variant: "destructive", + title: t("siteErrorCreate"), + description: t("remoteExitNodeRequired") + }); + setCreateLoading(false); + return; + } + + payload = { + ...payload, + exitNodeId: data.exitNodeId + }; + } const res = await api .put< @@ -533,7 +582,7 @@ WantedBy=default.target` currentNewtVersion = latestVersion; setNewtVersion(latestVersion); } catch (error) { - if (error instanceof Error && error.name === 'AbortError') { + if (error instanceof Error && error.name === "AbortError") { console.error(t("newtErrorFetchTimeout")); } else { console.error( @@ -558,8 +607,10 @@ WantedBy=default.target` await api .get(`/org/${orgId}/pick-site-defaults`) .catch((e) => { - // update the default value of the form to be local method - form.setValue("method", "local"); + // update the default value of the form to be local method only if local sites are not disabled + if (!env.flags.disableLocalSites) { + form.setValue("method", "local"); + } }) .then((res) => { if (res && res.status === 200) { @@ -602,6 +653,37 @@ WantedBy=default.target` } }); + if (build === "saas") { + // Fetch remote exit nodes for local sites + try { + const remoteExitNodesRes = await api.get< + AxiosResponse + >(`/org/${orgId}/remote-exit-nodes`); + + if ( + remoteExitNodesRes && + remoteExitNodesRes.status === 200 + ) { + const exitNodes = + remoteExitNodesRes.data.data.remoteExitNodes; + + // Convert to options for StrategySelect + const exitNodeOptions: RemoteExitNodeOption[] = + exitNodes + .filter((node) => node.exitNodeId !== null) + .map((node) => ({ + id: node.exitNodeId!.toString(), + title: node.name, + description: `${node.address?.split("/")[0] || "N/A"} - ${node.endpoint || "N/A"}` + })); + + setRemoteExitNodeOptions(exitNodeOptions); + } + } catch (error) { + console.error("Failed to fetch remote exit nodes:", error); + } + } + setLoadingPage(false); }; @@ -613,6 +695,18 @@ WantedBy=default.target` form.setValue("acceptClients", acceptClients); }, [acceptClients, form]); + // Sync form exitNodeId value with local state + useEffect(() => { + if (build !== "saas") { + // dont update the form + return; + } + form.setValue( + "exitNodeId", + selectedExitNodeId ? parseInt(selectedExitNodeId) : undefined + ); + }, [selectedExitNodeId, form]); + return ( <>
@@ -920,7 +1014,7 @@ WantedBy=default.target`
)} + + {build == "saas" && + form.watch("method") === "local" && ( + + + + {t("remoteExitNodeSelection")} + + + {t( + "remoteExitNodeSelectionDescription" + )} + + + + {remoteExitNodeOptions.length > 0 ? ( + { + setSelectedExitNodeId( + value + ); + }} + cols={1} + /> + ) : ( + + + + {t( + "noRemoteExitNodesAvailable" + )} + + + {t( + "noRemoteExitNodesAvailableDescription" + )} + + + )} + + + )}
diff --git a/src/app/[orgId]/settings/sites/page.tsx b/src/app/[orgId]/settings/sites/page.tsx index a854083c..b95c7666 100644 --- a/src/app/[orgId]/settings/sites/page.tsx +++ b/src/app/[orgId]/settings/sites/page.tsx @@ -52,6 +52,8 @@ export default async function SitesPage(props: SitesPageProps) { online: site.online, newtVersion: site.newtVersion || undefined, newtUpdateAvailable: site.newtUpdateAvailable || false, + exitNodeName: site.exitNodeName || undefined, + exitNodeEndpoint: site.exitNodeEndpoint || undefined, }; }); diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx new file mode 100644 index 00000000..f9d1854e --- /dev/null +++ b/src/app/auth/(private)/org/page.tsx @@ -0,0 +1,193 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { formatAxiosError, priv } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { cache } from "react"; +import { verifySession } from "@app/lib/auth/verifySession"; +import { redirect } from "next/navigation"; +import { pullEnv } from "@app/lib/pullEnv"; +import { LoginFormIDP } from "@app/components/LoginForm"; +import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; +import { build } from "@server/build"; +import { headers } from "next/headers"; +import { + GetLoginPageResponse, + LoadLoginPageResponse +} from "@server/routers/private/loginPage"; +import IdpLoginButtons from "@app/components/private/IdpLoginButtons"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle +} from "@app/components/ui/card"; +import { Button } from "@app/components/ui/button"; +import Link from "next/link"; +import { getTranslations } from "next-intl/server"; +import { GetSessionTransferTokenRenponse } from "@server/routers/auth/privateGetSessionTransferToken"; +import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; +import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken"; +import { GetOrgTierResponse } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; + +export const dynamic = "force-dynamic"; + +export default async function OrgAuthPage(props: { + params: Promise<{}>; + searchParams: Promise<{ token?: string }>; +}) { + const params = await props.params; + const searchParams = await props.searchParams; + + const env = pullEnv(); + + const authHeader = await authCookieHeader(); + + if (searchParams.token) { + return ; + } + + const getUser = cache(verifySession); + const user = await getUser({ skipCheckVerifyEmail: true }); + + const allHeaders = await headers(); + const host = allHeaders.get("host"); + + const t = await getTranslations(); + + const expectedHost = env.app.dashboardUrl.split("//")[1]; + let loginPage: LoadLoginPageResponse | undefined; + if (host !== expectedHost) { + try { + const res = await priv.get>( + `/login-page?fullDomain=${host}` + ); + + if (res && res.status === 200) { + loginPage = res.data.data; + } + } catch (e) {} + + if (!loginPage) { + redirect(env.app.dashboardUrl); + } + + let subscriptionStatus: GetOrgTierResponse | null = null; + try { + const getSubscription = cache(() => + priv.get>( + `/org/${loginPage!.orgId}/billing/tier` + ) + ); + const subRes = await getSubscription(); + subscriptionStatus = subRes.data.data; + } catch {} + const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + + if (build === "saas" && !subscribed) { + redirect(env.app.dashboardUrl); + } + + if (user) { + let redirectToken: string | undefined; + try { + const res = await priv.post< + AxiosResponse + >(`/get-session-transfer-token`, {}, authHeader); + + if (res && res.status === 200) { + const newToken = res.data.data.token; + + redirectToken = newToken; + } + } catch (e) { + console.error( + formatAxiosError(e, "Failed to get transfer token") + ); + } + + if (redirectToken) { + redirect( + `${env.app.dashboardUrl}/auth/org?token=${redirectToken}` + ); + } + } + } else { + redirect(env.app.dashboardUrl); + } + + let loginIdps: LoginFormIDP[] = []; + if (build === "saas") { + const idpsRes = await cache( + async () => + await priv.get>( + `/org/${loginPage!.orgId}/idp` + ) + )(); + loginIdps = idpsRes.data.data.idps.map((idp) => ({ + idpId: idp.idpId, + name: idp.name, + variant: idp.variant + })) as LoginFormIDP[]; + } + + return ( +
+
+ + {t("poweredBy")}{" "} + + {env.branding.appName || "Pangolin"} + + +
+ + + {t("orgAuthSignInTitle")} + + {loginIdps.length > 0 + ? t("orgAuthChooseIdpDescription") + : ""} + + + + {loginIdps.length > 0 ? ( + + ) : ( +
+

+ {t("orgAuthNoIdpConfigured")} +

+ + + +
+ )} +
+
+
+ ); +} diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 2ff8d09a..5dfa72c3 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -1,10 +1,13 @@ -import { cookies } from "next/headers"; +import { cookies, headers } from "next/headers"; import ValidateOidcToken from "@app/components/ValidateOidcToken"; import { cache } from "react"; -import { priv } from "@app/lib/api"; +import { formatAxiosError, priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { GetIdpResponse } from "@server/routers/idp"; import { getTranslations } from "next-intl/server"; +import { pullEnv } from "@app/lib/pullEnv"; +import { LoadLoginPageResponse } from "@server/routers/private/loginPage"; +import { redirect } from "next/navigation"; export const dynamic = "force-dynamic"; @@ -33,10 +36,34 @@ export default async function Page(props: { return
{t('idpErrorNotFound')}
; } + const allHeaders = await headers(); + const host = allHeaders.get("host"); + const env = pullEnv(); + const expectedHost = env.app.dashboardUrl.split("//")[1]; + let loginPage: LoadLoginPageResponse | undefined; + if (host !== expectedHost) { + try { + const res = await priv.get>( + `/login-page?idpId=${foundIdp.idpId}&fullDomain=${host}` + ); + + if (res && res.status === 200) { + loginPage = res.data.data; + } + } catch (e) { + console.error(formatAxiosError(e)); + } + + if (!loginPage) { + redirect(env.app.dashboardUrl); + } + } + return ( <> await priv.get>("/idp") - )(); - const loginIdps = idpsRes.data.data.idps.map((idp) => ({ - idpId: idp.idpId, - name: idp.name, - variant: idp.variant - })) as LoginFormIDP[]; + let loginIdps: LoginFormIDP[] = []; + if (build !== "saas") { + const idpsRes = await cache( + async () => await priv.get>("/idp") + )(); + loginIdps = idpsRes.data.data.idps.map((idp) => ({ + idpId: idp.idpId, + name: idp.name, + variant: idp.type + })) as LoginFormIDP[]; + } const t = await getTranslations(); diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index b221f44a..3eaf1e86 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -3,7 +3,7 @@ import { GetExchangeTokenResponse } from "@server/routers/resource"; import ResourceAuthPortal from "@app/components/ResourceAuthPortal"; -import { internal, priv } from "@app/lib/api"; +import { formatAxiosError, internal, priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; import { cache } from "react"; @@ -15,7 +15,13 @@ import AccessToken from "@app/components/AccessToken"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; import { ListIdpsResponse } from "@server/routers/idp"; +import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; import AutoLoginHandler from "@app/components/AutoLoginHandler"; +import { build } from "@server/build"; +import { headers } from "next/headers"; +import { GetLoginPageResponse } from "@server/routers/private/loginPage"; +import { GetOrgTierResponse } from "@server/routers/private/billing"; +import { TierId } from "@server/lib/private/billing/tiers"; export const dynamic = "force-dynamic"; @@ -55,6 +61,45 @@ export default async function ResourceAuthPage(props: { ); } + let subscriptionStatus: GetOrgTierResponse | null = null; + if (build == "saas") { + try { + const getSubscription = cache(() => + priv.get>( + `/org/${authInfo.orgId}/billing/tier` + ) + ); + const subRes = await getSubscription(); + subscriptionStatus = subRes.data.data; + } catch {} + } + const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + + const allHeaders = await headers(); + const host = allHeaders.get("host"); + + const expectedHost = env.app.dashboardUrl.split("//")[1]; + if (host !== expectedHost) { + if (build === "saas" && !subscribed) { + redirect(env.app.dashboardUrl); + } + + let loginPage: GetLoginPageResponse | undefined; + try { + const res = await priv.get>( + `/login-page?resourceId=${authInfo.resourceId}&fullDomain=${host}` + ); + + if (res && res.status === 200) { + loginPage = res.data.data; + } + } catch (e) {} + + if (!loginPage) { + redirect(env.app.dashboardUrl); + } + } + let redirectUrl = authInfo.url; if (searchParams.redirect) { try { @@ -136,13 +181,31 @@ export default async function ResourceAuthPage(props: { ); } - const idpsRes = await cache( - async () => await priv.get>("/idp") - )(); - const loginIdps = idpsRes.data.data.idps.map((idp) => ({ - idpId: idp.idpId, - name: idp.name - })) as LoginFormIDP[]; + let loginIdps: LoginFormIDP[] = []; + if (build === "saas") { + if (subscribed) { + const idpsRes = await cache( + async () => + await priv.get>( + `/org/${authInfo!.orgId}/idp` + ) + )(); + loginIdps = idpsRes.data.data.idps.map((idp) => ({ + idpId: idp.idpId, + name: idp.name, + variant: idp.variant + })) as LoginFormIDP[]; + } + } else { + const idpsRes = await cache( + async () => await priv.get>("/idp") + )(); + loginIdps = idpsRes.data.data.idps.map((idp) => ({ + idpId: idp.idpId, + name: idp.name, + variant: idp.type + })) as LoginFormIDP[]; + } if (authInfo.skipToIdpId && authInfo.skipToIdpId !== null) { const idp = loginIdps.find((idp) => idp.idpId === authInfo.skipToIdpId); @@ -152,6 +215,7 @@ export default async function ResourceAuthPage(props: { resourceId={authInfo.resourceId} skipToIdpId={authInfo.skipToIdpId} redirectUrl={redirectUrl} + orgId={build == "saas" ? authInfo.orgId : undefined} /> ); } @@ -178,6 +242,7 @@ export default async function ResourceAuthPage(props: { }} redirect={redirectUrl} idps={loginIdps} + orgId={build === "saas" ? authInfo.orgId : undefined} />
)} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e8b9c681..48170da5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,6 +4,8 @@ import { Inter } from "next/font/google"; import { ThemeProvider } from "@app/providers/ThemeProvider"; import EnvProvider from "@app/providers/EnvProvider"; import { pullEnv } from "@app/lib/pullEnv"; +import ThemeDataProvider from "@app/providers/PrivateThemeDataProvider"; +import SplashImage from "@app/components/private/SplashImage"; import SupportStatusProvider from "@app/providers/SupporterStatusProvider"; import { priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; @@ -17,13 +19,24 @@ import { getLocale } from "next-intl/server"; import { Toaster } from "@app/components/ui/toaster"; export const metadata: Metadata = { - title: `Dashboard - Pangolin`, + title: `Dashboard - ${process.env.BRANDING_APP_NAME || "Pangolin"}`, description: "", + + ...(process.env.BRANDING_FAVICON_PATH + ? { + icons: { + icon: [ + { + url: process.env.BRANDING_FAVICON_PATH as string + } + ] + } + } + : {}) }; export const dynamic = "force-dynamic"; -// const font = Figtree({ subsets: ["latin"] }); const font = Inter({ subsets: ["latin"] }); export default async function RootLayout({ @@ -62,25 +75,44 @@ export default async function RootLayout({ enableSystem disableTransitionOnChange > - - - + + - {/* Main content */} -
-
- - {children} + + {/* Main content */} +
+
+ + + {children} + + +
-
- - - - + + + + + ); -} \ No newline at end of file +} + +function loadBrandingColors() { + // this is loaded once on the server and not included in pullEnv + // so we don't need to parse the json every time pullEnv is called + if (process.env.BRANDING_COLORS) { + try { + return JSON.parse(process.env.BRANDING_COLORS); + } catch (e) { + console.error("Failed to parse BRANDING_COLORS", e); + } + } +} diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index f77bf3a9..369de1d4 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -14,6 +14,7 @@ import { User, Globe, // Added from 'dev' branch MonitorUp, // Added from 'dev' branch + Server, Zap } from "lucide-react"; @@ -57,6 +58,15 @@ export const orgNavSections = ( } ] : []), + ...(build == "saas" + ? [ + { + title: "sidebarRemoteExitNodes", + href: "/{orgId}/settings/remote-exit-nodes", + icon: + } + ] + : []), { title: "sidebarDomains", href: "/{orgId}/settings/domains", @@ -82,6 +92,15 @@ export const orgNavSections = ( href: "/{orgId}/settings/access/invitations", icon: }, + ...(build == "saas" + ? [ + { + title: "sidebarIdentityProviders", + href: "/{orgId}/settings/idp", + icon: + } + ] + : []), { title: "sidebarShareableLinks", href: "/{orgId}/settings/share-links", @@ -97,6 +116,15 @@ export const orgNavSections = ( href: "/{orgId}/settings/api-keys", icon: }, + ...(build == "saas" + ? [ + { + title: "sidebarBilling", + href: "/{orgId}/settings/billing", + icon: + } + ] + : []), { title: "sidebarSettings", href: "/{orgId}/settings/general", diff --git a/src/app/page.tsx b/src/app/page.tsx index 06b6b61c..2db1b6b1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -32,7 +32,7 @@ export default async function Page(props: { let complete = false; try { const setupRes = await internal.get< - AxiosResponse + AxiosResponse >(`/auth/initial-setup-complete`, await authCookieHeader()); complete = setupRes.data.data.complete; } catch (e) {} @@ -83,7 +83,10 @@ export default async function Page(props: { if (lastOrgExists) { redirect(`/${lastOrgCookie}`); } else { - const ownedOrg = orgs.find((org) => org.isOwner); + let ownedOrg = orgs.find((org) => org.isOwner); + if (!ownedOrg) { + ownedOrg = orgs[0]; + } if (ownedOrg) { redirect(`/${ownedOrg.orgId}`); } else { diff --git a/src/app/setup/layout.tsx b/src/app/setup/layout.tsx index 06dd3300..d2854c0c 100644 --- a/src/app/setup/layout.tsx +++ b/src/app/setup/layout.tsx @@ -12,7 +12,7 @@ import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; export const metadata: Metadata = { - title: `Setup - Pangolin`, + title: `Setup - ${process.env.BRANDING_APP_NAME || "Pangolin"}`, description: "" }; diff --git a/src/components/AutoLoginHandler.tsx b/src/components/AutoLoginHandler.tsx index f7183076..2391ece6 100644 --- a/src/components/AutoLoginHandler.tsx +++ b/src/components/AutoLoginHandler.tsx @@ -21,12 +21,14 @@ type AutoLoginHandlerProps = { resourceId: number; skipToIdpId: number; redirectUrl: string; + orgId?: string; }; export default function AutoLoginHandler({ resourceId, skipToIdpId, - redirectUrl + redirectUrl, + orgId }: AutoLoginHandlerProps) { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -44,7 +46,8 @@ export default function AutoLoginHandler({ try { const response = await generateOidcUrlProxy( skipToIdpId, - redirectUrl + redirectUrl, + orgId ); if (response.error) { @@ -83,7 +86,9 @@ export default function AutoLoginHandler({ {t("autoLoginTitle")} - {t("autoLoginDescription")} + + {t("autoLoginDescription")} + {loading && ( diff --git a/src/components/BrandingLogo.tsx b/src/components/BrandingLogo.tsx index 34771333..4a5330c8 100644 --- a/src/components/BrandingLogo.tsx +++ b/src/components/BrandingLogo.tsx @@ -27,10 +27,12 @@ export default function BrandingLogo(props: BrandingLogoProps) { } if (lightOrDark === "light") { - return "/logo/word_mark_black.png"; + return ( + env.branding.logo?.lightPath || "/logo/word_mark_black.png" + ); } - return "/logo/word_mark_white.png"; + return env.branding.logo?.darkPath || "/logo/word_mark_white.png"; } const path = getPath(); diff --git a/src/components/DashboardLoginForm.tsx b/src/components/DashboardLoginForm.tsx index 2a98ab0b..7be37db5 100644 --- a/src/components/DashboardLoginForm.tsx +++ b/src/components/DashboardLoginForm.tsx @@ -38,7 +38,10 @@ export default function DashboardLoginForm({
- +

{getSubtitle()}

diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index c14374d5..dccef529 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -32,6 +32,7 @@ import { createApiClient, formatAxiosError } from "@/lib/api"; import { useEnvContext } from "@/hooks/useEnvContext"; import { toast } from "@/hooks/useToast"; import { ListDomainsResponse } from "@server/routers/domain/listDomains"; +import { CheckDomainAvailabilityResponse } from "@server/routers/domain/privateCheckDomainNamespaceAvailability"; import { AxiosResponse } from "axios"; import { cn } from "@/lib/cn"; import { useTranslations } from "next-intl"; @@ -155,7 +156,10 @@ export default function DomainPicker2({ fullDomain: firstOrgDomain.baseDomain, baseDomain: firstOrgDomain.baseDomain }); - } else if ((build === "saas" || build === "enterprise") && !hideFreeDomain) { + } else if ( + (build === "saas" || build === "enterprise") && + !hideFreeDomain + ) { // If no organization domains, select the provided domain option const domainOptionText = build === "enterprise" @@ -198,7 +202,21 @@ export default function DomainPicker2({ .toLowerCase() .replace(/\./g, "-") .replace(/[^a-z0-9-]/g, "") - .replace(/-+/g, "-"); + .replace(/-+/g, "-") // Replace multiple consecutive dashes with single dash + .replace(/^-|-$/g, ""); // Remove leading/trailing dashes + + if (build != "oss") { + const response = await api.get< + AxiosResponse + >( + `/domain/check-namespace-availability?subdomain=${encodeURIComponent(checkSubdomain)}` + ); + + if (response.status === 200) { + const { options } = response.data.data; + setAvailableOptions(options); + } + } } catch (error) { console.error("Failed to check domain availability:", error); setAvailableOptions([]); @@ -272,13 +290,16 @@ export default function DomainPicker2({ toast({ variant: "destructive", title: t("domainPickerInvalidSubdomain"), - description: t("domainPickerInvalidSubdomainRemoved", { sub }), + description: t("domainPickerInvalidSubdomainRemoved", { sub }) }); return ""; } const ok = validateByDomainType(sanitized, { - type: base.type === "provided-search" ? "provided-search" : "organization", + type: + base.type === "provided-search" + ? "provided-search" + : "organization", domainType: base.domainType }); @@ -286,7 +307,10 @@ export default function DomainPicker2({ toast({ variant: "destructive", title: t("domainPickerInvalidSubdomain"), - description: t("domainPickerInvalidSubdomainCannotMakeValid", { sub, domain: base.domain }), + description: t("domainPickerInvalidSubdomainCannotMakeValid", { + sub, + domain: base.domain + }) }); return ""; } @@ -294,7 +318,10 @@ export default function DomainPicker2({ if (sub !== sanitized) { toast({ title: t("domainPickerSubdomainSanitized"), - description: t("domainPickerSubdomainCorrected", { sub, sanitized }), + description: t("domainPickerSubdomainCorrected", { + sub, + sanitized + }) }); } @@ -365,7 +392,8 @@ export default function DomainPicker2({ onDomainChange?.({ domainId: option.domainId || "", domainNamespaceId: option.domainNamespaceId, - type: option.type === "provided-search" ? "provided" : "organization", + type: + option.type === "provided-search" ? "provided" : "organization", subdomain: sub || undefined, fullDomain, baseDomain: option.domain @@ -389,12 +417,16 @@ export default function DomainPicker2({ }); }; - const isSubdomainValid = selectedBaseDomain && subdomainInput - ? validateByDomainType(subdomainInput, { - type: selectedBaseDomain.type === "provided-search" ? "provided-search" : "organization", - domainType: selectedBaseDomain.domainType - }) - : true; + const isSubdomainValid = + selectedBaseDomain && subdomainInput + ? validateByDomainType(subdomainInput, { + type: + selectedBaseDomain.type === "provided-search" + ? "provided-search" + : "organization", + domainType: selectedBaseDomain.domainType + }) + : true; const showSubdomainInput = selectedBaseDomain && @@ -415,7 +447,6 @@ export default function DomainPicker2({ const hasMoreProvided = sortedAvailableOptions.length > providedDomainsShown; - return (
@@ -434,16 +465,16 @@ export default function DomainPicker2({ showProvidedDomainSearch ? "" : showSubdomainInput - ? "" - : t("domainPickerNotAvailableForCname") + ? "" + : t("domainPickerNotAvailableForCname") } disabled={ !showSubdomainInput && !showProvidedDomainSearch } className={cn( !isSubdomainValid && - subdomainInput && - "border-red-500 focus:border-red-500" + subdomainInput && + "border-red-500 focus:border-red-500" )} onChange={(e) => { if (showProvidedDomainSearch) { @@ -453,11 +484,13 @@ export default function DomainPicker2({ } }} /> - {showSubdomainInput && subdomainInput && !isValidSubdomainStructure(subdomainInput) && ( -

- {t("domainPickerInvalidSubdomainStructure")} -

- )} + {showSubdomainInput && + subdomainInput && + !isValidSubdomainStructure(subdomainInput) && ( +

+ {t("domainPickerInvalidSubdomainStructure")} +

+ )} {showSubdomainInput && !subdomainInput && (

{t("domainPickerEnterSubdomainOrLeaveBlank")} @@ -483,7 +516,7 @@ export default function DomainPicker2({ {selectedBaseDomain ? (

{selectedBaseDomain.type === - "organization" ? null : ( + "organization" ? null : ( )} @@ -557,8 +590,12 @@ export default function DomainPicker2({ {orgDomain.type.toUpperCase()}{" "} •{" "} {orgDomain.verified - ? t("domainPickerVerified") - : t("domainPickerUnverified")} + ? t( + "domainPickerVerified" + ) + : t( + "domainPickerUnverified" + )}
{(build === "saas" || - build === "enterprise") && !hideFreeDomain && ( + build === "enterprise") && + !hideFreeDomain && ( )} )} - {(build === "saas" || - build === "enterprise") && !hideFreeDomain && ( + {(build === "saas" || build === "enterprise") && + !hideFreeDomain && ( @@ -602,9 +642,13 @@ export default function DomainPicker2({ id: "provided-search", domain: build === - "enterprise" - ? t("domainPickerProvidedDomain") - : t("domainPickerFreeProvidedDomain"), + "enterprise" + ? t( + "domainPickerProvidedDomain" + ) + : t( + "domainPickerFreeProvidedDomain" + ), type: "provided-search" }) } @@ -615,9 +659,14 @@ export default function DomainPicker2({
- {build === "enterprise" - ? t("domainPickerProvidedDomain") - : t("domainPickerFreeProvidedDomain")} + {build === + "enterprise" + ? t( + "domainPickerProvidedDomain" + ) + : t( + "domainPickerFreeProvidedDomain" + )} {t( @@ -644,6 +693,15 @@ export default function DomainPicker2({
+ {/*showProvidedDomainSearch && build === "saas" && ( + + + + {t("domainPickerNotWorkSelfHosted")} + + + )*/} + {showProvidedDomainSearch && (
{isChecking && ( @@ -693,7 +751,7 @@ export default function DomainPicker2({ htmlFor={option.domainNamespaceId} data-state={ selectedProvidedDomain?.domainNamespaceId === - option.domainNamespaceId + option.domainNamespaceId ? "checked" : "unchecked" } diff --git a/src/components/HealthCheckDialog.tsx b/src/components/HealthCheckDialog.tsx new file mode 100644 index 00000000..1942e1d8 --- /dev/null +++ b/src/components/HealthCheckDialog.tsx @@ -0,0 +1,580 @@ +"use client"; + +import { useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { HeadersInput } from "@app/components/HeadersInput"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/ui/form"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@/components/Credenza"; +import { toast } from "@/hooks/useToast"; +import { useTranslations } from "next-intl"; + +type HealthCheckConfig = { + hcEnabled: boolean; + hcPath: string; + hcMethod: string; + hcInterval: number; + hcTimeout: number; + hcStatus: number | null; + hcHeaders?: { name: string; value: string }[] | null; + hcScheme?: string; + hcHostname: string; + hcPort: number; + hcFollowRedirects: boolean; + hcMode: string; + hcUnhealthyInterval: number; +}; + +type HealthCheckDialogProps = { + open: boolean; + setOpen: (val: boolean) => void; + targetId: number; + targetAddress: string; + targetMethod?: string; + initialConfig?: Partial; + onChanges: (config: HealthCheckConfig) => Promise; +}; + +export default function HealthCheckDialog({ + open, + setOpen, + targetId, + targetAddress, + targetMethod, + initialConfig, + onChanges +}: HealthCheckDialogProps) { + const t = useTranslations(); + + const healthCheckSchema = z.object({ + hcEnabled: z.boolean(), + hcPath: z.string().min(1, { message: t("healthCheckPathRequired") }), + hcMethod: z + .string() + .min(1, { message: t("healthCheckMethodRequired") }), + hcInterval: z + .number() + .int() + .positive() + .min(5, { message: t("healthCheckIntervalMin") }), + hcTimeout: z + .number() + .int() + .positive() + .min(1, { message: t("healthCheckTimeoutMin") }), + hcStatus: z.number().int().positive().min(100).optional().nullable(), + hcHeaders: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional(), + hcScheme: z.string().optional(), + hcHostname: z.string(), + hcPort: z.number().positive().gt(0).lte(65535), + hcFollowRedirects: z.boolean(), + hcMode: z.string(), + hcUnhealthyInterval: z.number().int().positive().min(5) + }); + + const form = useForm>({ + resolver: zodResolver(healthCheckSchema), + defaultValues: {} + }); + + useEffect(() => { + if (!open) return; + + // Determine default scheme from target method + const getDefaultScheme = () => { + if (initialConfig?.hcScheme) { + return initialConfig.hcScheme; + } + // Default to target method if it's http or https, otherwise default to http + if (targetMethod === "https") { + return "https"; + } + return "http"; + }; + + form.reset({ + hcEnabled: initialConfig?.hcEnabled, + hcPath: initialConfig?.hcPath, + hcMethod: initialConfig?.hcMethod, + hcInterval: initialConfig?.hcInterval, + hcTimeout: initialConfig?.hcTimeout, + hcStatus: initialConfig?.hcStatus, + hcHeaders: initialConfig?.hcHeaders, + hcScheme: getDefaultScheme(), + hcHostname: initialConfig?.hcHostname, + hcPort: initialConfig?.hcPort, + hcFollowRedirects: initialConfig?.hcFollowRedirects, + hcMode: initialConfig?.hcMode, + hcUnhealthyInterval: initialConfig?.hcUnhealthyInterval + }); + }, [open]); + + const watchedEnabled = form.watch("hcEnabled"); + + const handleFieldChange = async (fieldName: string, value: any) => { + try { + const currentValues = form.getValues(); + const updatedValues = { ...currentValues, [fieldName]: value }; + await onChanges({ + ...updatedValues, + hcStatus: updatedValues.hcStatus || null + }); + } catch (error) { + toast({ + title: t("healthCheckError"), + description: t("healthCheckErrorDescription"), + variant: "destructive" + }); + } + }; + + return ( + + + + {t("configureHealthCheck")} + + {t("configureHealthCheckDescription", { + target: targetAddress + })} + + + +
+ + {/* Enable Health Checks */} + ( + +
+ + {t("enableHealthChecks")} + + + {t( + "enableHealthChecksDescription" + )} + +
+ + { + field.onChange(value); + handleFieldChange( + "hcEnabled", + value + ); + }} + /> + +
+ )} + /> + + {watchedEnabled && ( +
+
+ ( + + + {t("healthScheme")} + + + + + )} + /> + ( + + + {t("healthHostname")} + + + { + field.onChange( + e + ); + handleFieldChange( + "hcHostname", + e.target + .value + ); + }} + /> + + + + )} + /> + ( + + + {t("healthPort")} + + + { + const value = + parseInt( + e.target + .value + ); + field.onChange( + value + ); + handleFieldChange( + "hcPort", + value + ); + }} + /> + + + + )} + /> + ( + + + {t("healthCheckPath")} + + + { + field.onChange( + e + ); + handleFieldChange( + "hcPath", + e.target + .value + ); + }} + /> + + + + )} + /> +
+ + {/* HTTP Method */} + ( + + + {t("httpMethod")} + + + + + )} + /> + + {/* Check Interval, Timeout, and Retry Attempts */} +
+ ( + + + {t( + "healthyIntervalSeconds" + )} + + + { + const value = + parseInt( + e.target + .value + ); + field.onChange( + value + ); + handleFieldChange( + "hcInterval", + value + ); + }} + /> + + + + )} + /> + + ( + + + {t( + "unhealthyIntervalSeconds" + )} + + + { + const value = + parseInt( + e.target + .value + ); + field.onChange( + value + ); + handleFieldChange( + "hcUnhealthyInterval", + value + ); + }} + /> + + + + )} + /> + + ( + + + {t("timeoutSeconds")} + + + { + const value = + parseInt( + e.target + .value + ); + field.onChange( + value + ); + handleFieldChange( + "hcTimeout", + value + ); + }} + /> + + + + )} + /> + + + {t("timeIsInSeconds")} + +
+ + {/* Expected Response Codes */} + ( + + + {t("expectedResponseCodes")} + + + { + const value = + parseInt( + e.target + .value + ); + field.onChange( + value + ); + handleFieldChange( + "hcStatus", + value + ); + }} + /> + + + {t( + "expectedResponseCodesDescription" + )} + + + + )} + /> + + {/* Custom Headers */} + ( + + + {t("customHeaders")} + + + { + field.onChange(value); + handleFieldChange( + "hcHeaders", + value + ); + }} + rows={4} + /> + + + {t( + "customHeadersDescription" + )} + + + + )} + /> +
+ )} + + +
+ + + +
+
+ ); +} diff --git a/src/components/LayoutHeader.tsx b/src/components/LayoutHeader.tsx index 2584b259..36db7628 100644 --- a/src/components/LayoutHeader.tsx +++ b/src/components/LayoutHeader.tsx @@ -6,6 +6,10 @@ import Link from "next/link"; import ProfileIcon from "@app/components/ProfileIcon"; import ThemeSwitcher from "@app/components/ThemeSwitcher"; import { useTheme } from "next-themes"; +import BrandingLogo from "./BrandingLogo"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { Badge } from "./ui/badge"; +import { build } from "@server/build"; interface LayoutHeaderProps { showTopBar: boolean; @@ -14,6 +18,7 @@ interface LayoutHeaderProps { export function LayoutHeader({ showTopBar }: LayoutHeaderProps) { const { theme } = useTheme(); const [path, setPath] = useState(""); + const { env } = useEnvContext(); useEffect(() => { function getPath() { @@ -44,16 +49,18 @@ export function LayoutHeader({ showTopBar }: LayoutHeaderProps) {
- {path && ( - Pangolin - )} + + {/* {build === "saas" && ( + Cloud Beta + )} */}
{showTopBar && ( diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx index 5da9adb2..dafa31a9 100644 --- a/src/components/LayoutSidebar.tsx +++ b/src/components/LayoutSidebar.tsx @@ -57,6 +57,16 @@ export function LayoutSidebar({ setSidebarStateCookie(isSidebarCollapsed); }, [isSidebarCollapsed]); + function loadFooterLinks(): { text: string; href?: string }[] | undefined { + if (env.branding.footer) { + try { + return JSON.parse(env.branding.footer); + } catch (e) { + console.error("Failed to parse BRANDING_FOOTER", e); + } + } + } + return (
{!isSidebarCollapsed && (
-
- - {!isUnlocked() - ? t("communityEdition") - : t("commercialEdition")} - - -
- {env?.app?.version && ( -
- - v{env.app.version} - - -
+ {loadFooterLinks() ? ( + <> + {loadFooterLinks()!.map((link, index) => ( +
+ {link.href ? ( +
+ + {link.text} + + +
+ ) : ( +
+ {link.text} +
+ )} +
+ ))} + + ) : ( + <> +
+ + {!isUnlocked() + ? t("communityEdition") + : t("commercialEdition")} + + +
+ {env?.app?.version && ( +
+ + v{env.app.version} + + +
+ )} + )}
)} diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 437cc8b7..c55ad871 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; @@ -22,10 +22,7 @@ import { CardTitle } from "@app/components/ui/card"; import { Alert, AlertDescription } from "@app/components/ui/alert"; -import { LoginResponse } from "@server/routers/auth"; import { useRouter } from "next/navigation"; -import { AxiosResponse } from "axios"; -import { formatAxiosError } from "@app/lib/api"; import { LockIcon, FingerprintIcon } from "lucide-react"; import { createApiClient } from "@app/lib/api"; import { @@ -49,6 +46,9 @@ import { } from "@app/actions/server"; import { redirect as redirectTo } from "next/navigation"; import { useEnvContext } from "@app/hooks/useEnvContext"; +// @ts-ignore +import { loadReoScript } from "reodotdev"; +import { build } from "@server/build"; export type LoginFormIDP = { idpId: number; @@ -60,13 +60,18 @@ type LoginFormProps = { redirect?: string; onLogin?: () => void | Promise; idps?: LoginFormIDP[]; + orgId?: string; }; -export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { +export default function LoginForm({ + redirect, + onLogin, + idps, + orgId +}: LoginFormProps) { const router = useRouter(); const { env } = useEnvContext(); - const api = createApiClient({ env }); const [error, setError] = useState(null); @@ -77,10 +82,32 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const [showSecurityKeyPrompt, setShowSecurityKeyPrompt] = useState(false); const t = useTranslations(); - const currentHost = typeof window !== "undefined" ? window.location.hostname : ""; + const currentHost = + typeof window !== "undefined" ? window.location.hostname : ""; const expectedHost = new URL(env.app.dashboardUrl).host; const isExpectedHost = currentHost === expectedHost; + const [reo, setReo] = useState(undefined); + useEffect(() => { + async function init() { + if (env.app.environment !== "prod") { + return; + } + try { + const clientID = env.server.reoClientId; + const reoClient = await loadReoScript({ clientID }); + await reoClient.init({ clientID }); + setReo(reoClient); + } catch (e) { + console.error("Failed to load Reo script", e); + } + } + + if (build == "saas") { + init(); + } + }, []); + const formSchema = z.object({ email: z.string().email({ message: t("emailInvalid") }), password: z.string().min(8, { message: t("passwordRequirementsChars") }) @@ -183,26 +210,13 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { } } } catch (e: any) { - if (e.isAxiosError) { - setError( - formatAxiosError( - e, - t("securityKeyAuthError", { - defaultValue: - "Failed to authenticate with security key" - }) - ) - ); - } else { - console.error(e); - setError( - e.message || - t("securityKeyAuthError", { - defaultValue: - "Failed to authenticate with security key" - }) - ); - } + console.error(e); + setError( + t("securityKeyAuthError", { + defaultValue: + "An unexpected error occurred. Please try again." + }) + ); } finally { setLoading(false); setShowSecurityKeyPrompt(false); @@ -224,6 +238,18 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { code }); + try { + const identity = { + username: email, + type: "email" // can be one of email, github, linkedin, gmail, userID, + }; + if (reo) { + reo.identify(identity); + } + } catch (e) { + console.error("Reo identify error:", e); + } + if (response.error) { setError(response.message); return; @@ -253,7 +279,11 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { if (data.emailVerificationRequired) { if (!isExpectedHost) { - setError(t("emailVerificationRequired", { dashboardUrl: env.app.dashboardUrl })); + setError( + t("emailVerificationRequired", { + dashboardUrl: env.app.dashboardUrl + }) + ); return; } if (redirect) { @@ -266,7 +296,11 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { if (data.twoFactorSetupRequired) { if (!isExpectedHost) { - setError(t("twoFactorSetupRequired", { dashboardUrl: env.app.dashboardUrl })); + setError( + t("twoFactorSetupRequired", { + dashboardUrl: env.app.dashboardUrl + }) + ); return; } const setupUrl = `/auth/2fa/setup?email=${encodeURIComponent(email)}${redirect ? `&redirect=${encodeURIComponent(redirect)}` : ""}`; @@ -278,25 +312,13 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { await onLogin(); } } catch (e: any) { - if (e.isAxiosError) { - const errorMessage = formatAxiosError( - e, - t("loginError", { - defaultValue: "Failed to log in" - }) - ); - setError(errorMessage); - return; - } else { - console.error(e); - setError( - e.message || - t("loginError", { - defaultValue: "Failed to log in" - }) - ); - return; - } + console.error(e); + setError( + t("loginError", { + defaultValue: + "An unexpected error occurred. Please try again." + }) + ); } finally { setLoading(false); } @@ -307,7 +329,8 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { try { const data = await generateOidcUrlProxy( idpId, - redirect || "/" + redirect || "/", + orgId ); const url = data.data?.redirectUrl; if (data.error) { @@ -527,7 +550,8 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
{idps.map((idp) => { - const effectiveType = idp.variant || idp.name.toLowerCase(); + const effectiveType = + idp.variant || idp.name.toLowerCase(); return ( + ); + }, + cell: ({ row }) => { + const originalRow = row.original; + return ( +
+ {originalRow.exitNodeName} + {build == "saas" && originalRow.exitNodeName && + ['mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'].includes(originalRow.exitNodeName.toLowerCase()) && ( + Cloud + )} +
+ ); + }, + }, ...(env.flags.enableClients ? [{ accessorKey: "address", header: ({ column }: { column: Column }) => { diff --git a/src/components/ValidateOidcToken.tsx b/src/components/ValidateOidcToken.tsx index 7ba8e145..d4d9678d 100644 --- a/src/components/ValidateOidcToken.tsx +++ b/src/components/ValidateOidcToken.tsx @@ -2,7 +2,6 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { createApiClient, formatAxiosError } from "@app/lib/api"; -import { ValidateOidcUrlCallbackResponse } from "@server/routers/idp"; import { AxiosResponse } from "axios"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; @@ -26,6 +25,7 @@ type ValidateOidcTokenParams = { expectedState: string | undefined; stateCookie: string | undefined; idp: { name: string }; + loginPageId?: number; }; export default function ValidateOidcToken(props: ValidateOidcTokenParams) { @@ -44,7 +44,7 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { async function validate() { setLoading(true); - console.log(t('idpOidcTokenValidating'), { + console.log(t("idpOidcTokenValidating"), { code: props.code, expectedState: props.expectedState, stateCookie: props.stateCookie @@ -59,7 +59,8 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { props.idpId, props.code || "", props.expectedState || "", - props.stateCookie || "" + props.stateCookie || "", + props.loginPageId ); if (response.error) { @@ -78,7 +79,7 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { const redirectUrl = data.redirectUrl; if (!redirectUrl) { - router.push("/"); + router.push(env.app.dashboardUrl); } setLoading(false); @@ -89,8 +90,13 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { } else { router.push(data.redirectUrl); } - } catch (e) { - setError(formatAxiosError(e, t('idpErrorOidcTokenValidating'))); + } catch (e: any) { + console.error(e); + setError( + t("idpErrorOidcTokenValidating", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); } finally { setLoading(false); } @@ -103,20 +109,24 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) {
- {t('idpConnectingTo', {name: props.idp.name})} - {t('idpConnectingToDescription')} + + {t("idpConnectingTo", { name: props.idp.name })} + + + {t("idpConnectingToDescription")} + {loading && (
- {t('idpConnectingToProcess')} + {t("idpConnectingToProcess")}
)} {!loading && !error && (
- {t('idpConnectingToFinished')} + {t("idpConnectingToFinished")}
)} {error && ( @@ -124,7 +134,9 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) { - {t('idpErrorConnectingTo', {name: props.idp.name})} + {t("idpErrorConnectingTo", { + name: props.idp.name + })} {error} diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx new file mode 100644 index 00000000..1917c609 --- /dev/null +++ b/src/components/private/AuthPageSettings.tsx @@ -0,0 +1,538 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; +import { Button } from "@app/components/ui/button"; +import { useOrgContext } from "@app/hooks/useOrgContext"; +import { toast } from "@app/hooks/useToast"; +import { useState, useEffect, forwardRef, useImperativeHandle } from "react"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/ui/form"; +import { Label } from "@/components/ui/label"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { formatAxiosError } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { useRouter } from "next/navigation"; +import { + SettingsSection, + SettingsSectionHeader, + SettingsSectionTitle, + SettingsSectionDescription, + SettingsSectionBody, + SettingsSectionForm +} from "@app/components/Settings"; +import { useTranslations } from "next-intl"; +import { GetLoginPageResponse } from "@server/routers/private/loginPage"; +import { ListDomainsResponse } from "@server/routers/domain"; +import { DomainRow } from "@app/components/DomainsTable"; +import { toUnicode } from "punycode"; +import { Globe, Trash2 } from "lucide-react"; +import CertificateStatus from "@app/components/private/CertificateStatus"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import DomainPicker from "@app/components/DomainPicker"; +import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; +import { InfoPopup } from "@app/components/ui/info-popup"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; +import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; +import { TierId } from "@server/lib/private/billing/tiers"; +import { build } from "@server/build"; + +// Auth page form schema +const AuthPageFormSchema = z.object({ + authPageDomainId: z.string().optional(), + authPageSubdomain: z.string().optional() +}); + +type AuthPageFormValues = z.infer; + +interface AuthPageSettingsProps { + onSaveSuccess?: () => void; + onSaveError?: (error: any) => void; +} + +export interface AuthPageSettingsRef { + saveAuthSettings: () => Promise; + hasUnsavedChanges: () => boolean; +} + +const AuthPageSettings = forwardRef(({ + onSaveSuccess, + onSaveError +}, ref) => { + const { org } = useOrgContext(); + const api = createApiClient(useEnvContext()); + const router = useRouter(); + const t = useTranslations(); + + const subscription = usePrivateSubscriptionStatusContext(); + const subscribed = subscription?.getTier() === TierId.STANDARD; + + // Auth page domain state + const [loginPage, setLoginPage] = useState( + null + ); + const [loginPageExists, setLoginPageExists] = useState(false); + const [editDomainOpen, setEditDomainOpen] = useState(false); + const [baseDomains, setBaseDomains] = useState([]); + const [selectedDomain, setSelectedDomain] = useState<{ + domainId: string; + subdomain?: string; + fullDomain: string; + baseDomain: string; + } | null>(null); + const [loadingLoginPage, setLoadingLoginPage] = useState(true); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [loadingSave, setLoadingSave] = useState(false); + + const form = useForm({ + resolver: zodResolver(AuthPageFormSchema), + defaultValues: { + authPageDomainId: loginPage?.domainId || "", + authPageSubdomain: loginPage?.subdomain || "" + }, + mode: "onChange" + }); + + // Expose save function to parent component + useImperativeHandle(ref, () => ({ + saveAuthSettings: async () => { + await form.handleSubmit(onSubmit)(); + }, + hasUnsavedChanges: () => hasUnsavedChanges + }), [form, hasUnsavedChanges]); + + // Fetch login page and domains data + useEffect(() => { + if (build !== "saas") { + return; + } + + const fetchLoginPage = async () => { + try { + const res = await api.get>( + `/org/${org?.org.orgId}/login-page` + ); + if (res.status === 200) { + setLoginPage(res.data.data); + setLoginPageExists(true); + // Update form with login page data + form.setValue( + "authPageDomainId", + res.data.data.domainId || "" + ); + form.setValue( + "authPageSubdomain", + res.data.data.subdomain || "" + ); + } + } catch (err) { + // Login page doesn't exist yet, that's okay + setLoginPage(null); + setLoginPageExists(false); + } finally { + setLoadingLoginPage(false); + } + }; + + const fetchDomains = async () => { + try { + const res = await api.get>( + `/org/${org?.org.orgId}/domains/` + ); + if (res.status === 200) { + const rawDomains = res.data.data.domains as DomainRow[]; + const domains = rawDomains.map((domain) => ({ + ...domain, + baseDomain: toUnicode(domain.baseDomain) + })); + setBaseDomains(domains); + } + } catch (err) { + console.error("Failed to fetch domains:", err); + } + }; + + if (org?.org.orgId) { + fetchLoginPage(); + fetchDomains(); + } + }, []); + + // Handle domain selection from modal + function handleDomainSelection(domain: { + domainId: string; + subdomain?: string; + fullDomain: string; + baseDomain: string; + }) { + form.setValue("authPageDomainId", domain.domainId); + form.setValue("authPageSubdomain", domain.subdomain || ""); + setEditDomainOpen(false); + + // Update loginPage state to show the selected domain immediately + const sanitizedSubdomain = domain.subdomain + ? finalizeSubdomainSanitize(domain.subdomain) + : ""; + + const sanitizedFullDomain = sanitizedSubdomain + ? `${sanitizedSubdomain}.${domain.baseDomain}` + : domain.baseDomain; + + // Only update loginPage state if a login page already exists + if (loginPageExists && loginPage) { + setLoginPage({ + ...loginPage, + domainId: domain.domainId, + subdomain: sanitizedSubdomain, + fullDomain: sanitizedFullDomain + }); + } + + setHasUnsavedChanges(true); + } + + // Clear auth page domain + function clearAuthPageDomain() { + form.setValue("authPageDomainId", ""); + form.setValue("authPageSubdomain", ""); + setLoginPage(null); + setHasUnsavedChanges(true); + } + + async function onSubmit(data: AuthPageFormValues) { + setLoadingSave(true); + + try { + // Handle auth page domain + if (data.authPageDomainId) { + if (build !== "saas" || (build === "saas" && subscribed)) { + const sanitizedSubdomain = data.authPageSubdomain + ? finalizeSubdomainSanitize(data.authPageSubdomain) + : ""; + + if (loginPageExists) { + // Login page exists on server - need to update it + // First, we need to get the loginPageId from the server since loginPage might be null locally + let loginPageId: number; + + if (loginPage) { + // We have the loginPage data locally + loginPageId = loginPage.loginPageId; + } else { + // User cleared selection locally, but login page still exists on server + // We need to fetch it to get the loginPageId + const fetchRes = await api.get< + AxiosResponse + >(`/org/${org?.org.orgId}/login-page`); + loginPageId = fetchRes.data.data.loginPageId; + } + + // Update existing auth page domain + const updateRes = await api.post( + `/org/${org?.org.orgId}/login-page/${loginPageId}`, + { + domainId: data.authPageDomainId, + subdomain: sanitizedSubdomain || null + } + ); + + if (updateRes.status === 201) { + setLoginPage(updateRes.data.data); + setLoginPageExists(true); + } + } else { + // No login page exists on server - create new one + const createRes = await api.put( + `/org/${org?.org.orgId}/login-page`, + { + domainId: data.authPageDomainId, + subdomain: sanitizedSubdomain || null + } + ); + + if (createRes.status === 201) { + setLoginPage(createRes.data.data); + setLoginPageExists(true); + } + } + } + } else if (loginPageExists) { + // Delete existing auth page domain if no domain selected + let loginPageId: number; + + if (loginPage) { + // We have the loginPage data locally + loginPageId = loginPage.loginPageId; + } else { + // User cleared selection locally, but login page still exists on server + // We need to fetch it to get the loginPageId + const fetchRes = await api.get< + AxiosResponse + >(`/org/${org?.org.orgId}/login-page`); + loginPageId = fetchRes.data.data.loginPageId; + } + + await api.delete( + `/org/${org?.org.orgId}/login-page/${loginPageId}` + ); + setLoginPage(null); + setLoginPageExists(false); + } + + setHasUnsavedChanges(false); + router.refresh(); + onSaveSuccess?.(); + } catch (e) { + toast({ + variant: "destructive", + title: t("authPageErrorUpdate"), + description: formatAxiosError(e, t("authPageErrorUpdateMessage")) + }); + onSaveError?.(e); + } finally { + setLoadingSave(false); + } + } + + return ( + <> + + + + {t("authPage")} + + + {t("authPageDescription")} + + + + {build === "saas" && !subscribed ? ( + + + {t("orgAuthPageDisabled")}{" "} + {t("subscriptionRequiredToUse")} + + + ) : null} + + + {loadingLoginPage ? ( +
+
+ {t("loading")} +
+
+ ) : ( +
+ +
+ +
+ + + {loginPage && + !loginPage.domainId ? ( + + ) : loginPage?.fullDomain ? ( + + {`${window.location.protocol}//${loginPage.fullDomain}`} + + ) : form.watch( + "authPageDomainId" + ) ? ( + // Show selected domain from form state when no loginPage exists yet + (() => { + const selectedDomainId = + form.watch( + "authPageDomainId" + ); + const selectedSubdomain = + form.watch( + "authPageSubdomain" + ); + const domain = + baseDomains.find( + (d) => + d.domainId === + selectedDomainId + ); + if (domain) { + const sanitizedSubdomain = + selectedSubdomain + ? finalizeSubdomainSanitize( + selectedSubdomain + ) + : ""; + const fullDomain = + sanitizedSubdomain + ? `${sanitizedSubdomain}.${domain.baseDomain}` + : domain.baseDomain; + return fullDomain; + } + return t("noDomainSet"); + })() + ) : ( + t("noDomainSet") + )} + +
+ + {form.watch("authPageDomainId") && ( + + )} +
+
+ + {/* Certificate Status */} + {(build !== "saas" || + (build === "saas" && subscribed)) && + loginPage?.domainId && + loginPage?.fullDomain && + !hasUnsavedChanges && ( + + )} + + {!form.watch("authPageDomainId") && ( +
+ {t( + "addDomainToEnableCustomAuthPages" + )} +
+ )} +
+
+ + )} +
+
+
+ + {/* Domain Picker Modal */} + setEditDomainOpen(setOpen)} + > + + + + {loginPage + ? t("editAuthPageDomain") + : t("setAuthPageDomain")} + + + {t("selectDomainForOrgAuthPage")} + + + + { + const selected = { + domainId: res.domainId, + subdomain: res.subdomain, + fullDomain: res.fullDomain, + baseDomain: res.baseDomain + }; + setSelectedDomain(selected); + }} + /> + + + + + + + + + + + ); +}); + +AuthPageSettings.displayName = 'AuthPageSettings'; + +export default AuthPageSettings; \ No newline at end of file diff --git a/src/components/private/AutoProvisionConfigWidget.tsx b/src/components/private/AutoProvisionConfigWidget.tsx new file mode 100644 index 00000000..35800ccc --- /dev/null +++ b/src/components/private/AutoProvisionConfigWidget.tsx @@ -0,0 +1,185 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { + FormField, + FormItem, + FormLabel, + FormControl, + FormDescription, + FormMessage +} from "@app/components/ui/form"; +import { SwitchInput } from "@app/components/SwitchInput"; +import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@app/components/ui/select"; +import { Input } from "@app/components/ui/input"; +import { useTranslations } from "next-intl"; +import { Control, FieldValues, Path } from "react-hook-form"; + +type Role = { + roleId: number; + name: string; +}; + +type AutoProvisionConfigWidgetProps = { + control: Control; + autoProvision: boolean; + onAutoProvisionChange: (checked: boolean) => void; + roleMappingMode: "role" | "expression"; + onRoleMappingModeChange: (mode: "role" | "expression") => void; + roles: Role[]; + roleIdFieldName: Path; + roleMappingFieldName: Path; +}; + +export default function AutoProvisionConfigWidget({ + control, + autoProvision, + onAutoProvisionChange, + roleMappingMode, + onRoleMappingModeChange, + roles, + roleIdFieldName, + roleMappingFieldName +}: AutoProvisionConfigWidgetProps) { + const t = useTranslations(); + + return ( +
+
+ + + {t("idpAutoProvisionUsersDescription")} + +
+ + {autoProvision && ( +
+
+ + {t("roleMapping")} + + + {t("roleMappingDescription")} + + + +
+ + +
+
+ + +
+
+
+ + {roleMappingMode === "role" ? ( + ( + + + + {t("selectRoleDescription")} + + + + )} + /> + ) : ( + ( + + + + + + {t("roleMappingExpressionDescription")} + + + + )} + /> + )} +
+ )} +
+ ); +} diff --git a/src/components/private/CertificateStatus.tsx b/src/components/private/CertificateStatus.tsx new file mode 100644 index 00000000..1b872371 --- /dev/null +++ b/src/components/private/CertificateStatus.tsx @@ -0,0 +1,156 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { Button } from "@/components/ui/button"; +import { RotateCw } from "lucide-react"; +import { useCertificate } from "@app/hooks/privateUseCertificate"; +import { useTranslations } from "next-intl"; + +type CertificateStatusProps = { + orgId: string; + domainId: string; + fullDomain: string; + autoFetch?: boolean; + showLabel?: boolean; + className?: string; + onRefresh?: () => void; + polling?: boolean; + pollingInterval?: number; +}; + +export default function CertificateStatus({ + orgId, + domainId, + fullDomain, + autoFetch = true, + showLabel = true, + className = "", + onRefresh, + polling = false, + pollingInterval = 5000 +}: CertificateStatusProps) { + const t = useTranslations(); + const { cert, certLoading, certError, refreshing, refreshCert } = useCertificate({ + orgId, + domainId, + fullDomain, + autoFetch, + polling, + pollingInterval + }); + + const handleRefresh = async () => { + await refreshCert(); + onRefresh?.(); + }; + + const getStatusColor = (status: string) => { + switch (status) { + case "valid": + return "text-green-500"; + case "pending": + case "requested": + return "text-yellow-500"; + case "expired": + case "failed": + return "text-red-500"; + default: + return "text-muted-foreground"; + } + }; + + const shouldShowRefreshButton = (status: string, updatedAt: string) => { + return ( + status === "failed" || + status === "expired" || + (status === "requested" && + updatedAt && new Date(updatedAt).getTime() < Date.now() - 5 * 60 * 1000) + ); + }; + + if (certLoading) { + return ( +
+ {showLabel && ( + + {t("certificateStatus")}: + + )} + + {t("loading")} + +
+ ); + } + + if (certError) { + return ( +
+ {showLabel && ( + + {t("certificateStatus")}: + + )} + + {certError} + +
+ ); + } + + if (!cert) { + return ( +
+ {showLabel && ( + + {t("certificateStatus")}: + + )} + + {t("none", { defaultValue: "None" })} + +
+ ); + } + + return ( +
+ {showLabel && ( + + {t("certificateStatus")}: + + )} + + + {cert.status.charAt(0).toUpperCase() + cert.status.slice(1)} + {shouldShowRefreshButton(cert.status, cert.updatedAt) && ( + + )} + + +
+ ); +} diff --git a/src/components/private/IdpLoginButtons.tsx b/src/components/private/IdpLoginButtons.tsx new file mode 100644 index 00000000..95e9a9f3 --- /dev/null +++ b/src/components/private/IdpLoginButtons.tsx @@ -0,0 +1,135 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { useState } from "react"; +import { Button } from "@app/components/ui/button"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; +import { useTranslations } from "next-intl"; +import Image from "next/image"; +import { generateOidcUrlProxy, type GenerateOidcUrlResponse } from "@app/actions/server"; +import { redirect as redirectTo } from "next/navigation"; + +export type LoginFormIDP = { + idpId: number; + name: string; + variant?: string; +}; + +type IdpLoginButtonsProps = { + idps: LoginFormIDP[]; + redirect?: string; + orgId?: string; +}; + +export default function IdpLoginButtons({ + idps, + redirect, + orgId +}: IdpLoginButtonsProps) { + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const t = useTranslations(); + + async function loginWithIdp(idpId: number) { + setLoading(true); + setError(null); + + let redirectToUrl: string | undefined; + try { + const response = await generateOidcUrlProxy( + idpId, + redirect || "/", + orgId + ); + + if (response.error) { + setError(response.message); + setLoading(false); + return; + } + + const data = response.data; + console.log("Redirecting to:", data?.redirectUrl); + if (data?.redirectUrl) { + redirectToUrl = data.redirectUrl; + } + } catch (e: any) { + console.error(e); + setError( + t("loginError", { + defaultValue: "An unexpected error occurred. Please try again." + }) + ); + setLoading(false); + } + + if (redirectToUrl) { + redirectTo(redirectToUrl); + } + } + + if (!idps || idps.length === 0) { + return null; + } + + return ( +
+ {error && ( + + {error} + + )} + +
+ {idps.map((idp) => { + const effectiveType = idp.variant || idp.name.toLowerCase(); + + return ( + + ); + })} +
+
+ ); +} diff --git a/src/components/private/OrgIdpDataTable.tsx b/src/components/private/OrgIdpDataTable.tsx new file mode 100644 index 00000000..c98a6234 --- /dev/null +++ b/src/components/private/OrgIdpDataTable.tsx @@ -0,0 +1,45 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { DataTable } from "@app/components/ui/data-table"; +import { useTranslations } from "next-intl"; + +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; + onAdd?: () => void; +} + +export function IdpDataTable({ + columns, + data, + onAdd +}: DataTableProps) { + const t = useTranslations(); + + return ( + + ); +} diff --git a/src/components/private/OrgIdpTable.tsx b/src/components/private/OrgIdpTable.tsx new file mode 100644 index 00000000..f0e4d6c9 --- /dev/null +++ b/src/components/private/OrgIdpTable.tsx @@ -0,0 +1,219 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { IdpDataTable } from "@app/components/private/OrgIdpDataTable"; +import { Button } from "@app/components/ui/button"; +import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react"; +import { useState } from "react"; +import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import { toast } from "@app/hooks/useToast"; +import { formatAxiosError } from "@app/lib/api"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useRouter } from "next/navigation"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from "@app/components/ui/dropdown-menu"; +import Link from "next/link"; +import { useTranslations } from "next-intl"; +import IdpTypeBadge from "@app/components/IdpTypeBadge"; + +export type IdpRow = { + idpId: number; + name: string; + type: string; + variant?: string; +}; + +type Props = { + idps: IdpRow[]; + orgId: string; +}; + +export default function IdpTable({ idps, orgId }: Props) { + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedIdp, setSelectedIdp] = useState(null); + const api = createApiClient(useEnvContext()); + const router = useRouter(); + const t = useTranslations(); + + const deleteIdp = async (idpId: number) => { + try { + await api.delete(`/org/${orgId}/idp/${idpId}`); + toast({ + title: t("success"), + description: t("idpDeletedDescription") + }); + setIsDeleteModalOpen(false); + router.refresh(); + } catch (e) { + toast({ + title: t("error"), + description: formatAxiosError(e), + variant: "destructive" + }); + } + }; + + + const columns: ColumnDef[] = [ + { + accessorKey: "idpId", + header: ({ column }) => { + return ( + + ); + } + }, + { + accessorKey: "name", + header: ({ column }) => { + return ( + + ); + } + }, + { + accessorKey: "type", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const type = row.original.type; + const variant = row.original.variant; + return ( + + ); + } + }, + { + id: "actions", + cell: ({ row }) => { + const siteRow = row.original; + return ( +
+ + + + + + + + {t("viewSettings")} + + + { + setSelectedIdp(siteRow); + setIsDeleteModalOpen(true); + }} + > + + {t("delete")} + + + + + + + +
+ ); + } + } + ]; + + return ( + <> + {selectedIdp && ( + { + setIsDeleteModalOpen(val); + setSelectedIdp(null); + }} + dialog={ +
+

+ {t("idpQuestionRemove", { + name: selectedIdp.name + })} +

+

+ {t("idpMessageRemove")} +

+

{t("idpMessageConfirm")}

+
+ } + buttonText={t("idpConfirmDelete")} + onConfirm={async () => deleteIdp(selectedIdp.idpId)} + string={selectedIdp.name} + title={t("idpDelete")} + /> + )} + + router.push(`/${orgId}/settings/idp/create`)} + /> + + ); +} diff --git a/src/components/private/RegionSelector.tsx b/src/components/private/RegionSelector.tsx new file mode 100644 index 00000000..56dde743 --- /dev/null +++ b/src/components/private/RegionSelector.tsx @@ -0,0 +1,101 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { useState } from "react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@app/components/ui/select"; +import { InfoPopup } from "@app/components/ui/info-popup"; +import { useTranslations } from "next-intl"; + +type Region = { + value: string; + label: string; + flag: string; +}; + +const regions: Region[] = [ + { + value: "us", + label: "North America", + flag: "" + }, + { + value: "eu", + label: "Europe", + flag: "" + } +]; + +export default function RegionSelector() { + const [selectedRegion, setSelectedRegion] = useState("us"); + const t = useTranslations(); + + const handleRegionChange = (value: string) => { + setSelectedRegion(value); + const region = regions.find((r) => r.value === value); + if (region) { + console.log(`Selected region: ${region.label}`); + } + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/components/private/SplashImage.tsx b/src/components/private/SplashImage.tsx new file mode 100644 index 00000000..a2063692 --- /dev/null +++ b/src/components/private/SplashImage.tsx @@ -0,0 +1,57 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { usePathname } from "next/navigation"; +import Image from "next/image"; + +type SplashImageProps = { + children: React.ReactNode; +}; + +export default function SplashImage({ children }: SplashImageProps) { + const pathname = usePathname(); + const { env } = useEnvContext(); + + function showBackgroundImage() { + if (!env.branding.background_image_path) { + return false; + } + const pathsPrefixes = ["/auth/login", "/auth/signup", "/auth/resource"]; + for (const prefix of pathsPrefixes) { + if (pathname.startsWith(prefix)) { + return true; + } + } + return false; + } + + return ( + <> + {showBackgroundImage() && ( + Background + )} + + {children} + + ); +} diff --git a/src/components/private/ValidateSessionTransferToken.tsx b/src/components/private/ValidateSessionTransferToken.tsx new file mode 100644 index 00000000..116785d8 --- /dev/null +++ b/src/components/private/ValidateSessionTransferToken.tsx @@ -0,0 +1,84 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { redirect, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { AlertCircle } from "lucide-react"; +import { useTranslations } from "next-intl"; +import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; + +type ValidateSessionTransferTokenParams = { + token: string; +}; + +export default function ValidateSessionTransferToken( + props: ValidateSessionTransferTokenParams +) { + const { env } = useEnvContext(); + const api = createApiClient({ env }); + const router = useRouter(); + + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const t = useTranslations(); + + useEffect(() => { + async function validate() { + setLoading(true); + + let doRedirect = false; + try { + const res = await api.post< + AxiosResponse + >(`/auth/transfer-session-token`, { + token: props.token + }); + + if (res && res.status === 200) { + doRedirect = true; + } + } catch (e) { + console.error(e); + setError(formatAxiosError(e, "Failed to validate token")); + } finally { + setLoading(false); + } + + if (doRedirect) { + redirect(env.app.dashboardUrl); + } + } + + validate(); + }, []); + + return ( +
+ {error && ( + + + + {error} + + + )} +
+ ); +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx index 2c30ee73..dff7777c 100644 --- a/src/components/ui/alert.tsx +++ b/src/components/ui/alert.tsx @@ -14,7 +14,9 @@ const alertVariants = cva( "border-destructive/50 border bg-destructive/10 text-destructive dark:border-destructive [&>svg]:text-destructive", success: "border-green-500/50 border bg-green-500/10 text-green-500 dark:border-success [&>svg]:text-green-500", - info: "border-blue-500/50 border bg-blue-500/10 text-blue-500 dark:border-blue-400 [&>svg]:text-blue-500" + info: "border-blue-500/50 border bg-blue-500/10 text-blue-500 dark:border-blue-400 [&>svg]:text-blue-500", + warning: + "border-yellow-500/50 border bg-yellow-500/10 text-yellow-500 dark:border-yellow-400 [&>svg]:text-yellow-500" } }, defaultVariants: { diff --git a/src/components/ui/toaster.tsx b/src/components/ui/toaster.tsx index ca64fc06..7a1d7a23 100644 --- a/src/components/ui/toaster.tsx +++ b/src/components/ui/toaster.tsx @@ -9,10 +9,13 @@ import { ToastTitle, ToastViewport } from "@/components/ui/toast"; +import { useEnvContext } from "@app/hooks/useEnvContext"; export function Toaster() { const { toasts } = useToast(); + const { env } = useEnvContext(); + return ( {toasts.map(function ({ diff --git a/src/contexts/privateRemoteExitNodeContext.ts b/src/contexts/privateRemoteExitNodeContext.ts new file mode 100644 index 00000000..65567b7c --- /dev/null +++ b/src/contexts/privateRemoteExitNodeContext.ts @@ -0,0 +1,24 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import { createContext } from "react"; + +type RemoteExitNodeContextType = { + remoteExitNode: GetRemoteExitNodeResponse; + updateRemoteExitNode: (updatedRemoteExitNode: Partial) => void; +}; + +const RemoteExitNodeContext = createContext(undefined); + +export default RemoteExitNodeContext; diff --git a/src/contexts/privateSubscriptionStatusContext.ts b/src/contexts/privateSubscriptionStatusContext.ts new file mode 100644 index 00000000..aa99c21f --- /dev/null +++ b/src/contexts/privateSubscriptionStatusContext.ts @@ -0,0 +1,28 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; +import { createContext } from "react"; + +type SubscriptionStatusContextType = { + subscriptionStatus: GetOrgSubscriptionResponse | null; + updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; + isActive: () => boolean; + getTier: () => string | null; +}; + +const PrivateSubscriptionStatusContext = createContext< + SubscriptionStatusContextType | undefined +>(undefined); + +export default PrivateSubscriptionStatusContext; diff --git a/src/hooks/privateUseCertificate.ts b/src/hooks/privateUseCertificate.ts new file mode 100644 index 00000000..7956c3c7 --- /dev/null +++ b/src/hooks/privateUseCertificate.ts @@ -0,0 +1,135 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import { useState, useCallback, useEffect } from "react"; +import { AxiosResponse } from "axios"; +import { GetCertificateResponse } from "@server/routers/private/certificates"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; + +type UseCertificateProps = { + orgId: string; + domainId: string; + fullDomain: string; + autoFetch?: boolean; + polling?: boolean; + pollingInterval?: number; +}; + +type UseCertificateReturn = { + cert: GetCertificateResponse | null; + certLoading: boolean; + certError: string | null; + refreshing: boolean; + fetchCert: () => Promise; + refreshCert: () => Promise; + clearCert: () => void; +}; + +export function useCertificate({ + orgId, + domainId, + fullDomain, + autoFetch = true, + polling = false, + pollingInterval = 5000 +}: UseCertificateProps): UseCertificateReturn { + const api = createApiClient(useEnvContext()); + + const [cert, setCert] = useState(null); + const [certLoading, setCertLoading] = useState(false); + const [certError, setCertError] = useState(null); + const [refreshing, setRefreshing] = useState(false); + + const fetchCert = useCallback(async (showLoading = true) => { + if (!orgId || !domainId || !fullDomain) return; + + if (showLoading) { + setCertLoading(true); + } + setCertError(null); + try { + const res = await api.get>( + `/org/${orgId}/certificate/${domainId}/${fullDomain}` + ); + const certData = res.data.data; + if (certData) { + setCert(certData); + } + } catch (error: any) { + console.error("Failed to fetch certificate:", error); + setCertError("Failed to fetch certificate"); + } finally { + if (showLoading) { + setCertLoading(false); + } + } + }, [api, orgId, domainId, fullDomain]); + + const refreshCert = useCallback(async () => { + if (!cert) return; + + setRefreshing(true); + setCertError(null); + try { + await api.post( + `/org/${orgId}/certificate/${cert.certId}/restart`, + {} + ); + // Update status to pending + setTimeout(() => { + setCert({ ...cert, status: "pending" }); + }, 500); + } catch (error: any) { + console.error("Failed to restart certificate:", error); + setCertError("Failed to restart certificate"); + } finally { + setRefreshing(false); + } + }, [api, orgId, cert]); + + const clearCert = useCallback(() => { + setCert(null); + setCertError(null); + }, []); + + // Auto-fetch on mount if enabled + useEffect(() => { + if (autoFetch && orgId && domainId && fullDomain) { + fetchCert(); + } + }, [autoFetch, orgId, domainId, fullDomain, fetchCert]); + + // Polling effect + useEffect(() => { + if (!polling || !orgId || !domainId || !fullDomain) return; + + const interval = setInterval(() => { + fetchCert(false); // Don't show loading for polling + }, pollingInterval); + + return () => clearInterval(interval); + }, [polling, orgId, domainId, fullDomain, pollingInterval, fetchCert]); + + return { + cert, + certLoading, + certError, + refreshing, + fetchCert, + refreshCert, + clearCert + }; +} diff --git a/src/hooks/privateUseRemoteExitNodeContext.ts b/src/hooks/privateUseRemoteExitNodeContext.ts new file mode 100644 index 00000000..8decdb36 --- /dev/null +++ b/src/hooks/privateUseRemoteExitNodeContext.ts @@ -0,0 +1,29 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function useRemoteExitNodeContext() { + if (build == "oss") { + return null; + } + const context = useContext(RemoteExitNodeContext); + if (context === undefined) { + throw new Error("useRemoteExitNodeContext must be used within a RemoteExitNodeProvider"); + } + return context; +} diff --git a/src/hooks/privateUseSubscriptionStatusContext.ts b/src/hooks/privateUseSubscriptionStatusContext.ts new file mode 100644 index 00000000..a6a53ac9 --- /dev/null +++ b/src/hooks/privateUseSubscriptionStatusContext.ts @@ -0,0 +1,29 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function usePrivateSubscriptionStatusContext() { + if (build == "oss") { + return null; + } + const context = useContext(PrivateSubscriptionStatusContext); + if (context === undefined) { + throw new Error( + "usePrivateSubscriptionStatusContext must be used within an PrivateSubscriptionStatusProvider" + ); + } + return context; +} diff --git a/src/lib/privateThemeColors.ts b/src/lib/privateThemeColors.ts new file mode 100644 index 00000000..0543d68a --- /dev/null +++ b/src/lib/privateThemeColors.ts @@ -0,0 +1,87 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +const defaultTheme = { + light: { + background: "oklch(0.99 0 0)", + foreground: "oklch(0.141 0.005 285.823)", + card: "oklch(1 0 0)", + "card-foreground": "oklch(0.141 0.005 285.823)", + popover: "oklch(1 0 0)", + "popover-foreground": "oklch(0.141 0.005 285.823)", + primary: "oklch(0.6717 0.1946 41.93)", + "primary-foreground": "oklch(0.98 0.016 73.684)", + secondary: "oklch(0.967 0.001 286.375)", + "secondary-foreground": "oklch(0.21 0.006 285.885)", + muted: "oklch(0.967 0.001 286.375)", + "muted-foreground": "oklch(0.552 0.016 285.938)", + accent: "oklch(0.967 0.001 286.375)", + "accent-foreground": "oklch(0.21 0.006 285.885)", + destructive: "oklch(0.577 0.245 27.325)", + border: "oklch(0.92 0.004 286.32)", + input: "oklch(0.92 0.004 286.32)", + ring: "oklch(0.705 0.213 47.604)", + radius: "0.65rem", + "chart-1": "oklch(0.646 0.222 41.116)", + "chart-2": "oklch(0.6 0.118 184.704)", + "chart-3": "oklch(0.398 0.07 227.392)", + "chart-4": "oklch(0.828 0.189 84.429)", + "chart-5": "oklch(0.769 0.188 70.08)" + }, + dark: { + background: "oklch(0.20 0.006 285.885)", + foreground: "oklch(0.985 0 0)", + card: "oklch(0.21 0.006 285.885)", + "card-foreground": "oklch(0.985 0 0)", + popover: "oklch(0.21 0.006 285.885)", + "popover-foreground": "oklch(0.985 0 0)", + primary: "oklch(0.6717 0.1946 41.93)", + "primary-foreground": "oklch(0.98 0.016 73.684)", + secondary: "oklch(0.274 0.006 286.033)", + "secondary-foreground": "oklch(0.985 0 0)", + muted: "oklch(0.274 0.006 286.033)", + "muted-foreground": "oklch(0.705 0.015 286.067)", + accent: "oklch(0.274 0.006 286.033)", + "accent-foreground": "oklch(0.985 0 0)", + destructive: "oklch(0.704 0.191 22.216)", + border: "oklch(1 0 0 / 10%)", + input: "oklch(1 0 0 / 15%)", + ring: "oklch(0.646 0.222 41.116)", + "chart-1": "oklch(0.488 0.243 264.376)", + "chart-2": "oklch(0.696 0.17 162.48)", + "chart-3": "oklch(0.769 0.188 70.08)", + "chart-4": "oklch(0.627 0.265 303.9)", + "chart-5": "oklch(0.645 0.246 16.439)" + } +}; + +export default function setGlobalColorTheme( + themeMode: "light" | "dark", + colors: { + light: Record; + dark: Record; + } +) { + const merged = { + light: { ...defaultTheme.light, ...colors.light }, + dark: { ...defaultTheme.dark, ...colors.dark } + }; + + const theme = merged[themeMode] as { + [key: string]: string; + }; + + for (const key in theme) { + document.documentElement.style.setProperty(`--${key}`, theme[key]); + } +} diff --git a/src/lib/pullEnv.ts b/src/lib/pullEnv.ts index 39d8b66a..dfe22a87 100644 --- a/src/lib/pullEnv.ts +++ b/src/lib/pullEnv.ts @@ -13,10 +13,13 @@ export function pullEnv(): Env { resourceAccessTokenHeadersId: process.env .RESOURCE_ACCESS_TOKEN_HEADERS_ID as string, resourceAccessTokenHeadersToken: process.env - .RESOURCE_ACCESS_TOKEN_HEADERS_TOKEN as string + .RESOURCE_ACCESS_TOKEN_HEADERS_TOKEN as string, + reoClientId: process.env.REO_CLIENT_ID as string, + maxmind_db_path: process.env.MAXMIND_DB_PATH as string }, app: { environment: process.env.ENVIRONMENT as string, + sandbox_mode: process.env.SANDBOX_MODE === "true" ? true : false, version: process.env.APP_VERSION as string, dashboardUrl: process.env.DASHBOARD_URL as string }, @@ -47,5 +50,52 @@ export function pullEnv(): Env { hideSupporterKey: process.env.HIDE_SUPPORTER_KEY === "true" ? true : false }, + + branding: { + appName: process.env.BRANDING_APP_NAME as string, + background_image_path: process.env.BACKGROUND_IMAGE_PATH as string, + logo: { + lightPath: process.env.BRANDING_LOGO_LIGHT_PATH as string, + darkPath: process.env.BRANDING_LOGO_DARK_PATH as string, + authPage: { + width: parseInt( + process.env.BRANDING_LOGO_AUTH_WIDTH as string + ), + height: parseInt( + process.env.BRANDING_LOGO_AUTH_HEIGHT as string + ) + }, + navbar: { + width: parseInt( + process.env.BRANDING_LOGO_NAVBAR_WIDTH as string + ), + height: parseInt( + process.env.BRANDING_LOGO_NAVBAR_HEIGHT as string + ) + } + }, + loginPage: { + titleText: process.env.LOGIN_PAGE_TITLE_TEXT as string, + subtitleText: process.env.LOGIN_PAGE_SUBTITLE_TEXT as string + }, + signupPage: { + titleText: process.env.SIGNUP_PAGE_TITLE_TEXT as string, + subtitleText: process.env.SIGNUP_PAGE_SUBTITLE_TEXT as string + }, + resourceAuthPage: { + showLogo: + process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO === "true" + ? true + : false, + hidePoweredBy: + process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY === "true" + ? true + : false, + titleText: process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT as string, + subtitleText: process.env + .RESOURCE_AUTH_PAGE_SUBTITLE_TEXT as string + }, + footer: process.env.BRANDING_FOOTER as string + } }; } diff --git a/src/lib/types/env.ts b/src/lib/types/env.ts index 66686619..2c3c2479 100644 --- a/src/lib/types/env.ts +++ b/src/lib/types/env.ts @@ -1,6 +1,7 @@ export type Env = { app: { environment: string; + sandbox_mode: boolean; version: string; dashboardUrl: string; }; @@ -12,6 +13,8 @@ export type Env = { resourceSessionRequestParam: string; resourceAccessTokenHeadersId: string; resourceAccessTokenHeadersToken: string; + reoClientId?: string; + maxmind_db_path?: string; }; email: { emailEnabled: boolean; @@ -25,5 +28,36 @@ export type Env = { disableBasicWireguardSites: boolean; enableClients: boolean; hideSupporterKey: boolean; - } + }, + branding: { + appName?: string; + background_image_path?: string; + logo?: { + lightPath?: string; + darkPath?: string; + authPage?: { + width?: number; + height?: number; + }; + navbar?: { + width?: number; + height?: number; + } + }, + loginPage?: { + titleText?: string; + subtitleText?: string; + }, + signupPage?: { + titleText?: string; + subtitleText?: string; + }, + resourceAuthPage?: { + showLogo?: boolean; + hidePoweredBy?: boolean; + titleText?: string; + subtitleText?: string; + }, + footer?: string; + }; }; diff --git a/src/lib/types/privateThemeTypes.tsx b/src/lib/types/privateThemeTypes.tsx new file mode 100644 index 00000000..de0b2d2b --- /dev/null +++ b/src/lib/types/privateThemeTypes.tsx @@ -0,0 +1,13 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 00000000..41c6d1b3 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,42 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { build } from '@server/build'; + +export function middleware(request: NextRequest) { + // If build is OSS, block access to private routes + if (build === 'oss') { + const pathname = request.nextUrl.pathname; + + // Define private route patterns that should be blocked in OSS build + const privateRoutes = [ + '/settings/billing', + '/settings/remote-exit-nodes', + '/settings/idp', + '/auth/org' + ]; + + // Check if current path matches any private route pattern + const isPrivateRoute = privateRoutes.some(route => + pathname.includes(route) + ); + + if (isPrivateRoute) { + // Return 404 to make it seem like the route doesn't exist + return new NextResponse(null, { status: 404 }); + } + } + + return NextResponse.next(); +} + +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - api (API routes) + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + */ + '/((?!api|_next/static|_next/image|favicon.ico).*)', + ], +}; \ No newline at end of file diff --git a/src/providers/PrivateRemoteExitNodeProvider.tsx b/src/providers/PrivateRemoteExitNodeProvider.tsx new file mode 100644 index 00000000..8dfd8f1a --- /dev/null +++ b/src/providers/PrivateRemoteExitNodeProvider.tsx @@ -0,0 +1,56 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; +import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import { useState } from "react"; +import { useTranslations } from "next-intl"; + +type RemoteExitNodeProviderProps = { + children: React.ReactNode; + remoteExitNode: GetRemoteExitNodeResponse; +}; + +export function RemoteExitNodeProvider({ + children, + remoteExitNode: serverRemoteExitNode +}: RemoteExitNodeProviderProps) { + const [remoteExitNode, setRemoteExitNode] = useState(serverRemoteExitNode); + + const t = useTranslations(); + + const updateRemoteExitNode = (updatedRemoteExitNode: Partial) => { + if (!remoteExitNode) { + throw new Error(t('remoteExitNodeErrorNoUpdate')); + } + setRemoteExitNode((prev) => { + if (!prev) { + return prev; + } + return { + ...prev, + ...updatedRemoteExitNode + }; + }); + }; + + return ( + + {children} + + ); +} + +export default RemoteExitNodeProvider; diff --git a/src/providers/PrivateSubscriptionStatusProvider.tsx b/src/providers/PrivateSubscriptionStatusProvider.tsx new file mode 100644 index 00000000..9f4c5cd2 --- /dev/null +++ b/src/providers/PrivateSubscriptionStatusProvider.tsx @@ -0,0 +1,84 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; +import { getTierPriceSet } from "@server/lib/private/billing/tiers"; +import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; +import { useState } from "react"; + +interface ProviderProps { + children: React.ReactNode; + subscriptionStatus: GetOrgSubscriptionResponse | null; + env: string; + sandbox_mode: boolean; +} + +export function PrivateSubscriptionStatusProvider({ + children, + subscriptionStatus, + env, + sandbox_mode +}: ProviderProps) { + const [subscriptionStatusState, setSubscriptionStatusState] = + useState(subscriptionStatus); + + const updateSubscriptionStatus = (updatedSubscriptionStatus: GetOrgSubscriptionResponse) => { + setSubscriptionStatusState((prev) => { + return { + ...updatedSubscriptionStatus + }; + }); + }; + + const isActive = () => { + if (subscriptionStatus?.subscription?.status === "active") { + return true; + } + return false; + }; + + const getTier = () => { + const tierPriceSet = getTierPriceSet(env, sandbox_mode); + + if (subscriptionStatus?.items && subscriptionStatus.items.length > 0) { + // Iterate through tiers in order (earlier keys are higher tiers) + for (const [tierId, priceId] of Object.entries(tierPriceSet)) { + // Check if any subscription item matches this tier's price ID + const matchingItem = subscriptionStatus.items.find(item => item.priceId === priceId); + if (matchingItem) { + return tierId; + } + } + } + + console.log("No matching tier found"); + return null; + }; + + return ( + + {children} + + ); +} + +export default PrivateSubscriptionStatusProvider; diff --git a/src/providers/PrivateThemeDataProvider.tsx b/src/providers/PrivateThemeDataProvider.tsx new file mode 100644 index 00000000..a8da4551 --- /dev/null +++ b/src/providers/PrivateThemeDataProvider.tsx @@ -0,0 +1,59 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +"use client"; + +import setGlobalColorTheme from "@app/lib/privateThemeColors"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; + +type ThemeColorStateProps = { + children: React.ReactNode; + colors: any; +}; + +export default function ThemeDataProvider({ + children, + colors +}: ThemeColorStateProps) { + const [isMounted, setIsMounted] = useState(false); + const { theme } = useTheme(); + + useEffect(() => { + if (!colors) { + setIsMounted(true); + return; + } + + let lightOrDark = theme; + + if (theme === "system" || !theme) { + lightOrDark = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + } + + setGlobalColorTheme(lightOrDark as "light" | "dark", colors); + + if (!isMounted) { + setIsMounted(true); + } + }, [theme]); + + if (!isMounted) { + return null; + } + + return <>{children}; +} From a67aa3852d141383c02a34c0942a3bb98217c518 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 20:57:11 -0700 Subject: [PATCH 123/322] Remove config --- config/config.yml | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 config/config.yml diff --git a/config/config.yml b/config/config.yml deleted file mode 100644 index 9ae2a1e6..00000000 --- a/config/config.yml +++ /dev/null @@ -1,39 +0,0 @@ -app: - dashboard_url: "https://pangolin.internal" - log_level: debug -server: - cors: - origins: - - "https://pangolin.internal" - methods: - - "GET" - - "POST" - - "PUT" - - "DELETE" - - "PATCH" - allowed_headers: - - "X-CSRF-Token" - - "Content-Type" - credentials: true - secret: 1b5f55122e7611f0bf624bafe52c91daadsfjhksd234 -gerbil: - base_endpoint: pangolin.fosrl.io -flags: - require_email_verification: true - disable_signup_without_invite: false - disable_user_create_org: true - allow_base_domain_resources: false - disable_local_sites: false - disable_basic_wireguard_sites: true - enable_integration_api: true - disable_config_managed_domains: true - hide_supporter_key: true - enable_redis: true - enable_clients: true - allow_raw_resources: true -email: - smtp_host: "email-smtp.us-east-1.amazonaws.com" - smtp_port: 587 - smtp_user: "AKIATFBMPNE6PKLOK4MK" - smtp_pass: "BHStM/Nz9B9Crt3YePtsVDnjEp4MZmXqoQvZXWk0MQTC" - no_reply: no-reply@fossorial.io From 8fe42bc6aa611b23c11548689153917fd4bc1398 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 20:57:32 -0700 Subject: [PATCH 124/322] Update gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6a533ebb..53d00008 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,4 @@ server/db/index.ts server/build.ts postgres/ dynamic/ -certificates/ *.mmdb From ed64d4b5ae6902be9a393b3daf5ec0fd13d6e879 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sat, 4 Oct 2025 21:01:15 -0700 Subject: [PATCH 125/322] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 53d00008..f28ea6b9 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ server/build.ts postgres/ dynamic/ *.mmdb +config/config.yml From 8d7e5baf9da142aba037aa8a0dac68dcc2e48832 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 21:02:30 -0700 Subject: [PATCH 126/322] Update ignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f28ea6b9..661c32f9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ next-env.d.ts *-audit.json migrations tsconfig.tsbuildinfo +config/config.yml config/config.saas.yml config/config.oss.yml config/config.enterprise.yml @@ -46,5 +47,4 @@ server/db/index.ts server/build.ts postgres/ dynamic/ -*.mmdb -config/config.yml +*.mmdb \ No newline at end of file From cc7c44314539d0e839c3cbb58b603ee8185c2076 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 21:07:59 -0700 Subject: [PATCH 127/322] Update test --- .github/workflows/linting.yml | 9 +++++---- .github/workflows/test.yml | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index f2129300..1a01f1c4 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -26,9 +26,10 @@ jobs: node-version: '22' - name: Install dependencies - run: | - npm ci + run: npm ci + + - name: Create build file + run: npm run set:oss - name: Run ESLint - run: | - npx eslint . --ext .js,.jsx,.ts,.tsx \ No newline at end of file + run: npx eslint . --ext .js,.jsx,.ts,.tsx \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61315015..52a5f04a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,10 +24,10 @@ jobs: run: npm ci - name: Create database index.ts - run: echo 'export * from "./sqlite";' > server/db/index.ts + run: npm run set:sqlite - name: Create build file - run: echo 'export const build = 'oss' as any;' > server/build.ts + run: npm run set:oss - name: Generate database migrations run: npm run db:sqlite:generate From c5569fccf1263cefa3f9c2cd97b896ebfbc38374 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:14 -0700 Subject: [PATCH 128/322] New translations en-us.json (French) --- messages/fr-FR.json | 351 ++++++++++++++++++++++---------------------- 1 file changed, 178 insertions(+), 173 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 00da9036..95a90c0c 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.", "siteWg": "WireGuard basique", "siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.", - "siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Ressources locales seulement. Pas de tunneling.", - "siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Voir tous les sites", "siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site", "siteNewtCredentials": "Identifiants Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Ressource HTTPS", "resourceHTTPDescription": "Requêtes de proxy à votre application via HTTPS en utilisant un sous-domaine ou un domaine de base.", "resourceRaw": "Ressource TCP/UDP brute", - "resourceRawDescription": "Demandes de proxy à votre application via TCP/UDP en utilisant un numéro de port.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Créer une ressource", "resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource", "resourceSeeAll": "Voir toutes les ressources", @@ -168,9 +168,9 @@ "siteSelect": "Sélectionner un site", "siteSearch": "Chercher un site", "siteNotFound": "Aucun site trouvé.", - "selectCountry": "Sélectionnez un pays", - "searchCountries": "Recherchez des pays...", - "noCountryFound": "Aucun pays trouvé.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Ce site fournira la connectivité à la cible.", "resourceType": "Type de ressource", "resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Sous-domaine : {subdomain}", "domainPickerNamespace": "Espace de noms : {namespace}", "domainPickerShowMore": "Afficher plus", - "regionSelectorTitle": "Sélectionner Région", - "regionSelectorInfo": "Sélectionner une région nous aide à offrir de meilleures performances pour votre localisation. Vous n'avez pas besoin d'être dans la même région que votre serveur.", - "regionSelectorPlaceholder": "Choisissez une région", - "regionSelectorComingSoon": "Bientôt disponible", - "billingLoadingSubscription": "Chargement de l'abonnement...", - "billingFreeTier": "Niveau gratuit", - "billingWarningOverLimit": "Attention : Vous avez dépassé une ou plusieurs limites d'utilisation. Vos sites ne se connecteront pas tant que vous n'avez pas modifié votre abonnement ou ajusté votre utilisation.", - "billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation", - "billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@fossorial.io.", - "billingDataUsage": "Utilisation des données", - "billingOnlineTime": "Temps en ligne du site", - "billingUsers": "Utilisateurs actifs", - "billingDomains": "Domaines actifs", - "billingRemoteExitNodes": "Nœuds auto-hébergés actifs", - "billingNoLimitConfigured": "Aucune limite configurée", - "billingEstimatedPeriod": "Période de facturation estimée", - "billingIncludedUsage": "Utilisation incluse", - "billingIncludedUsageDescription": "Utilisation incluse dans votre plan d'abonnement actuel", - "billingFreeTierIncludedUsage": "Tolérances d'utilisation du niveau gratuit", - "billingIncluded": "inclus", - "billingEstimatedTotal": "Total estimé :", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", "billingNotes": "Notes", - "billingEstimateNote": "Ceci est une estimation basée sur votre utilisation actuelle.", - "billingActualChargesMayVary": "Les frais réels peuvent varier.", - "billingBilledAtEnd": "Vous serez facturé à la fin de la période de facturation.", - "billingModifySubscription": "Modifier l'abonnement", - "billingStartSubscription": "Démarrer l'abonnement", - "billingRecurringCharge": "Frais récurrents", - "billingManageSubscriptionSettings": "Gérez les paramètres et préférences de votre abonnement", - "billingNoActiveSubscription": "Vous n'avez pas d'abonnement actif. Commencez votre abonnement pour augmenter les limites d'utilisation.", - "billingFailedToLoadSubscription": "Échec du chargement de l'abonnement", - "billingFailedToLoadUsage": "Échec du chargement de l'utilisation", - "billingFailedToGetCheckoutUrl": "Échec pour obtenir l'URL de paiement", - "billingPleaseTryAgainLater": "Veuillez réessayer plus tard.", - "billingCheckoutError": "Erreur de paiement", - "billingFailedToGetPortalUrl": "Échec pour obtenir l'URL du portail", - "billingPortalError": "Erreur du portail", - "billingDataUsageInfo": "Vous êtes facturé pour toutes les données transférées via vos tunnels sécurisés lorsque vous êtes connecté au cloud. Cela inclut le trafic entrant et sortant sur tous vos sites. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre plan ou réduisiez l'utilisation. Les données ne sont pas facturées lors de l'utilisation de nœuds.", - "billingOnlineTimeInfo": "Vous êtes facturé en fonction de la durée de connexion de vos sites au cloud. Par exemple, 44 640 minutes équivaut à un site fonctionnant 24/7 pendant un mois complet. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre forfait ou réduisiez votre consommation. Le temps n'est pas facturé lors de l'utilisation de nœuds.", - "billingUsersInfo": "Vous êtes facturé pour chaque utilisateur dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes utilisateurs actifs dans votre organisation.", - "billingDomainInfo": "Vous êtes facturé pour chaque domaine dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes de domaine actifs dans votre organisation.", - "billingRemoteExitNodesInfo": "Vous êtes facturé pour chaque nœud géré dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domaine introuvable", "domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.", "failed": "Échec", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.", "resourcePortRequired": "Le numéro de port est requis pour les ressources non-HTTP", "resourcePortNotAllowed": "Le numéro de port ne doit pas être défini pour les ressources HTTP", - "billingPricingCalculatorLink": "Calculateur de prix", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Je suis d'accord avec", "termsOfService": "les conditions d'utilisation", @@ -1412,41 +1412,41 @@ "addNewTarget": "Ajouter une nouvelle cible", "targetsList": "Liste des cibles", "targetErrorDuplicateTargetFound": "Cible en double trouvée", - "healthCheckHealthy": "Sain", - "healthCheckUnhealthy": "En mauvaise santé", - "healthCheckUnknown": "Inconnu", - "healthCheck": "Vérification de l'état de santé", - "configureHealthCheck": "Configurer la vérification de l'état de santé", - "configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}", - "enableHealthChecks": "Activer les vérifications de santé", - "enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.", - "healthScheme": "Méthode", - "healthSelectScheme": "Sélectionnez la méthode", - "healthCheckPath": "Chemin d'accès", - "healthHostname": "IP / Hôte", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "Le chemin à vérifier pour le statut de santé.", - "healthyIntervalSeconds": "Intervalle sain", - "unhealthyIntervalSeconds": "Intervalle en mauvaise santé", - "IntervalSeconds": "Intervalle sain", - "timeoutSeconds": "Délai", - "timeIsInSeconds": "Le temps est exprimé en secondes", - "retryAttempts": "Tentatives de réessai", - "expectedResponseCodes": "Codes de réponse attendus", - "expectedResponseCodesDescription": "Code de statut HTTP indiquant un état de santé satisfaisant. Si non renseigné, 200-300 est considéré comme satisfaisant.", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "En-têtes personnalisés", - "customHeadersDescription": "En-têtes séparés par une nouvelle ligne: En-nom: valeur", - "headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.", - "saveHealthCheck": "Sauvegarder la vérification de l'état de santé", - "healthCheckSaved": "Vérification de l'état de santé enregistrée", - "healthCheckSavedDescription": "La configuration de la vérification de l'état de santé a été enregistrée avec succès", - "healthCheckError": "Erreur de vérification de l'état de santé", - "healthCheckErrorDescription": "Une erreur s'est produite lors de l'enregistrement de la configuration de la vérification de l'état de santé", - "healthCheckPathRequired": "Le chemin de vérification de l'état de santé est requis", - "healthCheckMethodRequired": "La méthode HTTP est requise", - "healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes", - "healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde", - "healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "Méthode HTTP", "selectHttpMethod": "Sélectionnez la méthode HTTP", "domainPickerSubdomainLabel": "Sous-domaine", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Entrez un sous-domaine pour rechercher et sélectionner parmi les domaines gratuits disponibles.", "domainPickerFreeDomains": "Domaines gratuits", "domainPickerSearchForAvailableDomains": "Rechercher des domaines disponibles", - "domainPickerNotWorkSelfHosted": "Remarque : Les domaines fournis gratuitement ne sont pas disponibles pour les instances auto-hébergées pour le moment.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domaine", "resourceEditDomain": "Modifier le domaine", "siteName": "Nom du site", @@ -1543,72 +1543,72 @@ "autoLoginError": "Erreur de connexion automatique", "autoLoginErrorNoRedirectUrl": "Aucune URL de redirection reçue du fournisseur d'identité.", "autoLoginErrorGeneratingUrl": "Échec de la génération de l'URL d'authentification.", - "remoteExitNodeManageRemoteExitNodes": "Gérer auto-hébergé", - "remoteExitNodeDescription": "Gérer les nœuds pour étendre votre connectivité réseau", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Rechercher des nœuds...", - "remoteExitNodeAdd": "Ajouter un noeud", - "remoteExitNodeErrorDelete": "Erreur lors de la suppression du noeud", - "remoteExitNodeQuestionRemove": "Êtes-vous sûr de vouloir supprimer le noeud {selectedNode} de l'organisation ?", - "remoteExitNodeMessageRemove": "Une fois supprimé, le noeud ne sera plus accessible.", - "remoteExitNodeMessageConfirm": "Pour confirmer, veuillez saisir le nom du noeud ci-dessous.", - "remoteExitNodeConfirmDelete": "Confirmer la suppression du noeud", - "remoteExitNodeDelete": "Supprimer le noeud", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Créer un noeud", - "description": "Créer un nouveau nœud pour étendre votre connectivité réseau", - "viewAllButton": "Voir tous les nœuds", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Stratégie de création", - "description": "Choisissez ceci pour configurer manuellement votre nœud ou générer de nouveaux identifiants.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adopter un nœud", - "description": "Choisissez ceci si vous avez déjà les identifiants pour le noeud." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Générer des clés", - "description": "Choisissez ceci si vous voulez générer de nouvelles clés pour le noeud" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adopter un nœud existant", - "description": "Entrez les identifiants du noeud existant que vous souhaitez adopter", - "nodeIdLabel": "Nœud ID", - "nodeIdDescription": "L'ID du noeud existant que vous voulez adopter", + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", "secretLabel": "Secret", - "secretDescription": "La clé secrète du noeud existant", - "submitButton": "Noeud d'Adopt" + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Informations d'identification générées", - "description": "Utilisez ces identifiants générés pour configurer votre noeud", - "nodeIdTitle": "Nœud ID", + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", "secretTitle": "Secret", - "saveCredentialsTitle": "Ajouter des identifiants à la config", - "saveCredentialsDescription": "Ajoutez ces informations d'identification à votre fichier de configuration du nœud Pangolin auto-hébergé pour compléter la connexion.", - "submitButton": "Créer un noeud" + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "ID de nœud et secret sont requis lors de l'adoption d'un noeud existant" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Échec du chargement des valeurs par défaut", - "defaultsNotLoaded": "Valeurs par défaut non chargées", - "createFailed": "Impossible de créer le noeud" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Noeud créé avec succès" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Sélection du noeud", - "remoteExitNodeSelectionDescription": "Sélectionnez un nœud pour acheminer le trafic pour ce site local", - "remoteExitNodeRequired": "Un noeud doit être sélectionné pour les sites locaux", - "noRemoteExitNodesAvailable": "Aucun noeud disponible", - "noRemoteExitNodesAvailableDescription": "Aucun noeud n'est disponible pour cette organisation. Créez d'abord un noeud pour utiliser des sites locaux.", - "exitNode": "Nœud de sortie", - "country": "Pays", - "rulesMatchCountry": "Actuellement basé sur l'IP source", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Gestion autonome", "description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Domaine international détecté", "willbestoredas": "Sera stocké comme :", - "roleMappingDescription": "Détermine comment les rôles sont assignés aux utilisateurs lorsqu'ils se connectent lorsque la fourniture automatique est activée.", - "selectRole": "Sélectionnez un rôle", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choisir un rôle", - "selectRoleDescription": "Sélectionnez un rôle à assigner à tous les utilisateurs de ce fournisseur d'identité", - "roleMappingExpressionDescription": "Entrez une expression JMESPath pour extraire les informations du rôle du jeton ID", - "idpTenantIdRequired": "L'ID du locataire est requis", - "invalidValue": "Valeur non valide", - "idpTypeLabel": "Type de fournisseur d'identité", - "roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'", - "idpGoogleConfiguration": "Configuration Google", - "idpGoogleConfigurationDescription": "Configurer vos identifiants Google OAuth2", - "idpGoogleClientIdDescription": "Votre identifiant client Google OAuth2", - "idpGoogleClientSecretDescription": "Votre secret client Google OAuth2", - "idpAzureConfiguration": "Configuration de l'entra ID Azure", - "idpAzureConfigurationDescription": "Configurer vos identifiants OAuth2 Azure Entra", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "votre-locataire-id", - "idpAzureTenantIdDescription": "Votre ID de locataire Azure (trouvé dans l'aperçu Azure Active Directory)", - "idpAzureClientIdDescription": "Votre ID client d'enregistrement de l'application Azure", - "idpAzureClientSecretDescription": "Le secret de votre client d'enregistrement Azure App", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Configuration Google", - "idpAzureConfigurationTitle": "Configuration de l'entra ID Azure", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Votre ID client d'enregistrement de l'application Azure", - "idpAzureClientSecretDescription2": "Le secret de votre client d'enregistrement Azure App", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Fournisseur Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Sous-réseau", - "subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.", - "authPage": "Page d'authentification", - "authPageDescription": "Configurer la page d'authentification de votre organisation", - "authPageDomain": "Domaine de la page d'authentification", - "noDomainSet": "Aucun domaine défini", - "changeDomain": "Changer de domaine", - "selectDomain": "Sélectionner un domaine", - "restartCertificate": "Redémarrer le certificat", - "editAuthPageDomain": "Modifier le domaine de la page d'authentification", - "setAuthPageDomain": "Définir le domaine de la page d'authentification", - "failedToFetchCertificate": "Impossible de récupérer le certificat", - "failedToRestartCertificate": "Échec du redémarrage du certificat", - "addDomainToEnableCustomAuthPages": "Ajouter un domaine pour activer les pages d'authentification personnalisées pour votre organisation", - "selectDomainForOrgAuthPage": "Sélectionnez un domaine pour la page d'authentification de l'organisation", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Domaine fourni", "domainPickerFreeProvidedDomain": "Domaine fourni gratuitement", "domainPickerVerified": "Vérifié", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "La «{sub}» n'a pas pu être validée pour {domain}.", "domainPickerSubdomainSanitized": "Sous-domaine nettoyé", "domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"", - "orgAuthSignInTitle": "Connectez-vous à votre organisation", - "orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer", - "orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.", - "orgAuthSignInWithPangolin": "Se connecter avec Pangolin", - "subscriptionRequiredToUse": "Un abonnement est requis pour utiliser cette fonctionnalité.", - "idpDisabled": "Les fournisseurs d'identité sont désactivés.", - "orgAuthPageDisabled": "La page d'authentification de l'organisation est désactivée.", - "domainRestartedDescription": "La vérification du domaine a été redémarrée avec succès", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", - "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici." + "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From b3a6cd066042b6fe12d7a95a33e5de434538ef7c Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:15 -0700 Subject: [PATCH 129/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 215 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 207 insertions(+), 8 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 1a579ed0..ba407ff2 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Най-лесният начин да създадете входна точка в мрежата си. Без допълнително конфигуриране.", "siteWg": "Основен WireGuard", "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES", - "siteWgDescriptionSaas": "Използвайте всеки WireGuard клиент за установяване на тунел. Ръчно нат задаване е необходимо. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Local resources only. No tunneling. ONLY WORKS ON SELF HOSTED NODES", - "siteLocalDescriptionSaas": "Само локални ресурси. Без тунелиране. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Вижте всички сайтове", "siteTunnelDescription": "Определете как искате да се свържете с вашия сайт", "siteNewtCredentials": "Newt Удостоверения", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS ресурс", "resourceHTTPDescription": "Прокси заявки към вашето приложение през HTTPS с помощта на субдомейн или базов домейн.", "resourceRaw": "Суров TCP/UDP ресурс", - "resourceRawDescription": "Прокси заявки към вашето приложение през TCP/UDP с помощта на номер на порт.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Създайте ресурс", "resourceCreateDescription": "Следвайте стъпките по-долу, за да създадете нов ресурс", "resourceSeeAll": "Вижте всички ресурси", @@ -168,6 +168,9 @@ "siteSelect": "Изберете сайт", "siteSearch": "Търсене на сайт", "siteNotFound": "Няма намерени сайтове.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Този сайт ще осигури свързаност до целта.", "resourceType": "Тип ресурс", "resourceTypeDescription": "Определете как искате да получите достъп до вашия ресурс", @@ -914,8 +917,6 @@ "idpConnectingToFinished": "Свързано", "idpErrorConnectingTo": "Имаше проблем със свързването към {name}. Моля, свържете се с вашия администратор.", "idpErrorNotFound": "Не е намерен идентификационен доставчик", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Невалидна покана", "inviteInvalidDescription": "Линкът към поканата е невалиден.", "inviteErrorWrongUser": "Поканата не е за този потребител", @@ -1257,6 +1258,48 @@ "domainPickerSubdomain": "Поддомейн: {subdomain}", "domainPickerNamespace": "Име на пространство: {namespace}", "domainPickerShowMore": "Покажи повече", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Домейнът не е намерен", "domainNotFoundDescription": "Този ресурс е деактивиран, защото домейнът вече не съществува в нашата система. Моля, задайте нов домейн за този ресурс.", "failed": "Неуспешно", @@ -1320,6 +1363,7 @@ "createDomainDnsPropagationDescription": "Промените в DNS може да отнемат време, за да се разпространят в интернет. Това може да отнеме от няколко минути до 48 часа, в зависимост от вашия DNS доставчик и TTL настройките .", "resourcePortRequired": "Номерът на порта е задължителен за не-HTTP ресурси", "resourcePortNotAllowed": "Номерът на порта не трябва да бъде задаван за HTTP ресурси", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Съгласен съм с", "termsOfService": "условията за ползване", @@ -1368,6 +1412,41 @@ "addNewTarget": "Добави нова цел", "targetsList": "Списък с цели", "targetErrorDuplicateTargetFound": "Дублирана цел намерена", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "customHeaders": "Персонализирани заглавия", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP Метод", "selectHttpMethod": "Изберете HTTP метод", "domainPickerSubdomainLabel": "Поддомен", @@ -1381,6 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Въведете поддомен, за да търсите и изберете от наличните свободни домейни.", "domainPickerFreeDomains": "Безплатни домейни", "domainPickerSearchForAvailableDomains": "Търсене за налични домейни", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Домейн", "resourceEditDomain": "Редактиране на домейн", "siteName": "Име на сайта", @@ -1463,6 +1543,72 @@ "autoLoginError": "Грешка при автоматично влизане", "autoLoginErrorNoRedirectUrl": "Не е получен URL за пренасочване от доставчика на идентификационни данни.", "autoLoginErrorGeneratingUrl": "Неуспешно генериране на URL за удостоверяване.", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", + "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeCreate": { + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", + "strategy": { + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", + "adopt": { + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." + }, + "generate": { + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" + } + }, + "adopt": { + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" + }, + "generate": { + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" + }, + "validation": { + "adoptRequired": "Node ID and Secret are required when adopting an existing node" + }, + "errors": { + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" + }, + "success": { + "created": "Node created successfully" + } + }, + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Управлявано Самостоятелно-хоствано", "description": "По-надежден и по-нисък поддръжка на Самостоятелно-хостван Панголиин сървър с допълнителни екстри", @@ -1501,11 +1647,53 @@ }, "internationaldomaindetected": "Открит международен домейн", "willbestoredas": "Ще бъде съхранено като:", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC доставчик", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик", - "customHeaders": "Персонализирани заглавия", - "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", - "headersValidationError": "Заглавията трябва да бъдат във формат: Име на заглавието: стойност.", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Предоставен домейн", "domainPickerFreeProvidedDomain": "Безплатен предоставен домейн", "domainPickerVerified": "Проверено", @@ -1519,10 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не може да се направи валиден за {domain}.", "domainPickerSubdomainSanitized": "Поддомен пречистен", "domainPickerSubdomainCorrected": "\"{sub}\" беше коригиран на \"{sanitized}\"", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Редактиране на файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактиране на файл: docker-compose.yml", "emailVerificationRequired": "Потвърждението на Email е необходимо. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 942f7c2bc9966b73a83c21285b32260b3935313b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:16 -0700 Subject: [PATCH 130/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 365 ++++++++++++++++++++++---------------------- 1 file changed, 185 insertions(+), 180 deletions(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 91f4a46a..fb6e2eb0 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Nejjednodušší způsob, jak vytvořit vstupní bod do vaší sítě. Žádné další nastavení.", "siteWg": "Základní WireGuard", "siteWgDescription": "Použijte jakéhokoli klienta WireGuard abyste sestavili tunel. Vyžaduje se ruční nastavení NAT.", - "siteWgDescriptionSaas": "Použijte jakéhokoli klienta WireGuard abyste sestavili tunel. Vyžaduje se ruční nastavení NAT. FUNGUJE POUZE NA SELF-HOSTED SERVERECH", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Pouze lokální zdroje. Žádný tunel.", - "siteLocalDescriptionSaas": "Pouze lokální zdroje. Žádný tunel. FUNGUJE POUZE NA SELF-HOSTED SERVERECH", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Zobrazit všechny lokality", "siteTunnelDescription": "Určete jak se chcete připojit k vaší lokalitě", "siteNewtCredentials": "Přihlašovací údaje Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Zdroj HTTPS", "resourceHTTPDescription": "Požadavky na proxy pro vaši aplikaci přes HTTPS pomocí subdomény nebo základní domény.", "resourceRaw": "Surový TCP/UDP zdroj", - "resourceRawDescription": "Požadavky na proxy pro vaši aplikaci přes TCP/UDP pomocí čísla portu.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Vytvořit zdroj", "resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj", "resourceSeeAll": "Zobrazit všechny zdroje", @@ -168,9 +168,9 @@ "siteSelect": "Vybrat lokalitu", "siteSearch": "Hledat lokalitu", "siteNotFound": "Nebyla nalezena žádná lokalita.", - "selectCountry": "Vyberte zemi", - "searchCountries": "Hledat země...", - "noCountryFound": "Nebyla nalezena žádná země.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Tato lokalita poskytne připojení k cíli.", "resourceType": "Typ zdroje", "resourceTypeDescription": "Určete, jak chcete přistupovat ke svému zdroji", @@ -917,8 +917,6 @@ "idpConnectingToFinished": "Připojeno", "idpErrorConnectingTo": "Při připojování k {name}došlo k chybě. Obraťte se na správce.", "idpErrorNotFound": "IdP nenalezen", - "idpGoogleAlt": "Google", - "idpAzureAlt": "Azure", "inviteInvalid": "Neplatná pozvánka", "inviteInvalidDescription": "Odkaz pro pozvání je neplatný.", "inviteErrorWrongUser": "Pozvat není pro tohoto uživatele", @@ -1260,48 +1258,48 @@ "domainPickerSubdomain": "Subdoména: {subdomain}", "domainPickerNamespace": "Jmenný prostor: {namespace}", "domainPickerShowMore": "Zobrazit více", - "regionSelectorTitle": "Vybrat region", - "regionSelectorInfo": "Výběr regionu nám pomáhá poskytovat lepší výkon pro vaši polohu. Nemusíte být ve stejném regionu jako váš server.", - "regionSelectorPlaceholder": "Vyberte region", - "regionSelectorComingSoon": "Již brzy", - "billingLoadingSubscription": "Načítání odběru...", - "billingFreeTier": "Volná úroveň", - "billingWarningOverLimit": "Upozornění: Překročili jste jeden nebo více omezení používání. Vaše stránky se nepřipojí dokud nezměníte předplatné nebo neupravíte své používání.", - "billingUsageLimitsOverview": "Přehled omezení použití", - "billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@fossorial.io.", - "billingDataUsage": "Využití dat", - "billingOnlineTime": "Stránka online čas", - "billingUsers": "Aktivní uživatelé", - "billingDomains": "Aktivní domény", - "billingRemoteExitNodes": "Aktivní Samostatně hostované uzly", - "billingNoLimitConfigured": "Žádný limit nenastaven", - "billingEstimatedPeriod": "Odhadované období fakturace", - "billingIncludedUsage": "Zahrnuto využití", - "billingIncludedUsageDescription": "Využití zahrnované s aktuálním plánem předplatného", - "billingFreeTierIncludedUsage": "Povolenky bezplatné úrovně využití", - "billingIncluded": "zahrnuto", - "billingEstimatedTotal": "Odhadovaný celkem:", - "billingNotes": "Poznámky", - "billingEstimateNote": "Toto je odhad založený na aktuálním využití.", - "billingActualChargesMayVary": "Skutečné náklady se mohou lišit.", - "billingBilledAtEnd": "Budete účtováni na konci fakturační doby.", - "billingModifySubscription": "Upravit předplatné", - "billingStartSubscription": "Začít předplatné", - "billingRecurringCharge": "Opakované nabití", - "billingManageSubscriptionSettings": "Spravovat nastavení a nastavení předplatného", - "billingNoActiveSubscription": "Nemáte aktivní předplatné. Začněte předplatné, abyste zvýšili omezení používání.", - "billingFailedToLoadSubscription": "Nepodařilo se načíst odběr", - "billingFailedToLoadUsage": "Nepodařilo se načíst využití", - "billingFailedToGetCheckoutUrl": "Nepodařilo se získat adresu URL pokladny", - "billingPleaseTryAgainLater": "Zkuste to prosím znovu později.", - "billingCheckoutError": "Chyba pokladny", - "billingFailedToGetPortalUrl": "Nepodařilo se získat URL portálu", - "billingPortalError": "Chyba portálu", - "billingDataUsageInfo": "Pokud jste připojeni k cloudu, jsou vám účtována všechna data přenášená prostřednictvím zabezpečených tunelů. To zahrnuje příchozí i odchozí provoz na všech vašich stránkách. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezmenšíte jeho používání. Data nejsou nabírána při používání uzlů.", - "billingOnlineTimeInfo": "Platíte na základě toho, jak dlouho budou vaše stránky připojeny k cloudu. Například, 44,640 minut se rovná jedné stránce 24/7 po celý měsíc. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezkrátíte jeho používání. Čas není vybírán při používání uzlů.", - "billingUsersInfo": "Obdrželi jste platbu za každého uživatele ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních uživatelských účtů ve vašem org.", - "billingDomainInfo": "Platba je účtována za každou doménu ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních doménových účtů na Vašem org.", - "billingRemoteExitNodesInfo": "Za každý spravovaný uzel ve vaší organizaci se vám účtuje denně. Fakturace je počítána na základě počtu aktivních spravovaných uzlů ve vašem org.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Doména nenalezena", "domainNotFoundDescription": "Tento dokument je zakázán, protože doména již neexistuje náš systém. Nastavte prosím novou doménu pro tento dokument.", "failed": "Selhalo", @@ -1365,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Změna DNS může trvat nějakou dobu, než se šíří po internetu. To může trvat kdekoli od několika minut do 48 hodin v závislosti na poskytovateli DNS a nastavení TTL.", "resourcePortRequired": "Pro neHTTP zdroje je vyžadováno číslo portu", "resourcePortNotAllowed": "Číslo portu by nemělo být nastaveno pro HTTP zdroje", - "billingPricingCalculatorLink": "Cenová kalkulačka", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Souhlasím s", "termsOfService": "podmínky služby", @@ -1414,41 +1412,41 @@ "addNewTarget": "Add New Target", "targetsList": "Seznam cílů", "targetErrorDuplicateTargetFound": "Byl nalezen duplicitní cíl", - "healthCheckHealthy": "Zdravé", - "healthCheckUnhealthy": "Nezdravé", - "healthCheckUnknown": "Neznámý", - "healthCheck": "Kontrola stavu", - "configureHealthCheck": "Konfigurace kontroly stavu", - "configureHealthCheckDescription": "Nastavit sledování zdravotního stavu pro {target}", - "enableHealthChecks": "Povolit kontrolu stavu", - "enableHealthChecksDescription": "Sledujte zdraví tohoto cíle. V případě potřeby můžete sledovat jiný cílový bod, než je cíl.", - "healthScheme": "Způsob", - "healthSelectScheme": "Vybrat metodu", - "healthCheckPath": "Cesta", - "healthHostname": "IP / Hostitel", - "healthPort": "Přístav", - "healthCheckPathDescription": "Cesta ke kontrole zdravotního stavu.", - "healthyIntervalSeconds": "Interval zdraví", - "unhealthyIntervalSeconds": "Nezdravý interval", - "IntervalSeconds": "Interval zdraví", - "timeoutSeconds": "Časový limit", - "timeIsInSeconds": "Čas je v sekundách", - "retryAttempts": "Opakovat pokusy", - "expectedResponseCodes": "Očekávané kódy odezvy", - "expectedResponseCodesDescription": "HTTP kód stavu, který označuje zdravý stav. Ponecháte-li prázdné, 200-300 je považováno za zdravé.", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Vlastní záhlaví", - "customHeadersDescription": "Záhlaví oddělená nová řádka: hodnota", - "headersValidationError": "Headers must be in the format: Header-Name: value.", - "saveHealthCheck": "Uložit kontrolu stavu", - "healthCheckSaved": "Kontrola stavu uložena", - "healthCheckSavedDescription": "Nastavení kontroly stavu bylo úspěšně uloženo", - "healthCheckError": "Chyba kontroly stavu", - "healthCheckErrorDescription": "Došlo k chybě při ukládání konfigurace kontroly stavu", - "healthCheckPathRequired": "Je vyžadována cesta kontroly stavu", - "healthCheckMethodRequired": "HTTP metoda je povinná", - "healthCheckIntervalMin": "Interval kontroly musí být nejméně 5 sekund", - "healthCheckTimeoutMin": "Časový limit musí být nejméně 1 sekunda", - "healthCheckRetryMin": "Pokusy opakovat musí být alespoň 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP metoda", "selectHttpMethod": "Vyberte HTTP metodu", "domainPickerSubdomainLabel": "Subdoména", @@ -1462,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Zadejte subdoménu pro hledání a výběr z dostupných domén zdarma.", "domainPickerFreeDomains": "Volné domény", "domainPickerSearchForAvailableDomains": "Hledat dostupné domény", - "domainPickerNotWorkSelfHosted": "Poznámka: Poskytnuté domény nejsou momentálně k dispozici pro vlastní hostované instance.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Doména", "resourceEditDomain": "Upravit doménu", "siteName": "Název webu", @@ -1545,72 +1543,72 @@ "autoLoginError": "Automatická chyba přihlášení", "autoLoginErrorNoRedirectUrl": "Od poskytovatele identity nebyla obdržena žádná adresa URL.", "autoLoginErrorGeneratingUrl": "Nepodařilo se vygenerovat ověřovací URL.", - "remoteExitNodeManageRemoteExitNodes": "Spravovat vlastní hostování", - "remoteExitNodeDescription": "Spravujte uzly pro rozšíření připojení k síti", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Hledat uzly...", - "remoteExitNodeAdd": "Přidat uzel", - "remoteExitNodeErrorDelete": "Chyba při odstraňování uzlu", - "remoteExitNodeQuestionRemove": "Jste si jisti, že chcete odstranit uzel {selectedNode} z organizace?", - "remoteExitNodeMessageRemove": "Po odstranění uzel již nebude přístupný.", - "remoteExitNodeMessageConfirm": "Pro potvrzení zadejte název uzlu níže.", - "remoteExitNodeConfirmDelete": "Potvrdit odstranění uzlu", - "remoteExitNodeDelete": "Odstranit uzel", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Vytvořit uzel", - "description": "Vytvořit nový uzel pro rozšíření síťového připojení", - "viewAllButton": "Zobrazit všechny uzly", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Strategie tvorby", - "description": "Vyberte pro manuální konfiguraci vašeho uzlu nebo vygenerujte nové přihlašovací údaje.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Přijmout uzel", - "description": "Zvolte tuto možnost, pokud již máte přihlašovací údaje k uzlu." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Generovat klíče", - "description": "Vyberte tuto možnost, pokud chcete vygenerovat nové klíče pro uzel" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Přijmout existující uzel", - "description": "Zadejte přihlašovací údaje existujícího uzlu, který chcete přijmout", - "nodeIdLabel": "ID uzlu", - "nodeIdDescription": "ID existujícího uzlu, který chcete přijmout", - "secretLabel": "Tajný klíč", - "secretDescription": "Tajný klíč existujícího uzlu", - "submitButton": "Přijmout uzel" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Vygenerovaná pověření", - "description": "Použijte tyto generované přihlašovací údaje pro nastavení vašeho uzlu", - "nodeIdTitle": "ID uzlu", - "secretTitle": "Tajný klíč", - "saveCredentialsTitle": "Přidat přihlašovací údaje do konfigurace", - "saveCredentialsDescription": "Přidejte tyto přihlašovací údaje do vlastního konfiguračního souboru Pangolin uzlu pro dokončení připojení.", - "submitButton": "Vytvořit uzel" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "ID uzlu a tajný klíč jsou vyžadovány při přijetí existujícího uzlu" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Nepodařilo se načíst výchozí hodnoty", - "defaultsNotLoaded": "Výchozí hodnoty nebyly načteny", - "createFailed": "Nepodařilo se vytvořit uzel" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Uzel byl úspěšně vytvořen" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Výběr uzlu", - "remoteExitNodeSelectionDescription": "Vyberte uzel pro směrování provozu přes tuto lokální stránku", - "remoteExitNodeRequired": "Pro lokální stránky musí být vybrán uzel", - "noRemoteExitNodesAvailable": "Nejsou k dispozici žádné uzly", - "noRemoteExitNodesAvailableDescription": "Pro tuto organizaci nejsou k dispozici žádné uzly. Nejprve vytvořte uzel pro použití lokálních stránek.", - "exitNode": "Ukončit uzel", - "country": "L 343, 22.12.2009, s. 1).", - "rulesMatchCountry": "Aktuálně založené na zdrojové IP adrese", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Spravované vlastní hostování", "description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami", @@ -1649,51 +1647,53 @@ }, "internationaldomaindetected": "Zjištěna mezinárodní doména", "willbestoredas": "Bude uloženo jako:", - "roleMappingDescription": "Určete, jak jsou role přiřazeny uživatelům, když se přihlásí, když je povoleno automatické poskytnutí služby.", - "selectRole": "Vyberte roli", - "roleMappingExpression": "Výraz", - "selectRolePlaceholder": "Vyberte roli", - "selectRoleDescription": "Vyberte roli pro přiřazení všem uživatelům od tohoto poskytovatele identity", - "roleMappingExpressionDescription": "Zadejte výraz JMESPath pro získání informací o roli z ID token", - "idpTenantIdRequired": "ID nájemce je povinné", - "invalidValue": "Neplatná hodnota", - "idpTypeLabel": "Typ poskytovatele identity", - "roleMappingExpressionPlaceholder": "např. obsahuje(skupiny, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Konfigurace Google", - "idpGoogleConfigurationDescription": "Konfigurace přihlašovacích údajů Google OAuth2", - "idpGoogleClientIdDescription": "Vaše ID klienta Google OAuth2", - "idpGoogleClientSecretDescription": "Tajný klíč klienta Google OAuth2", - "idpAzureConfiguration": "Nastavení Azure Entra ID", - "idpAzureConfigurationDescription": "Nastavte vaše Azure Entra ID OAuth2", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "vaše-tenant-id", - "idpAzureTenantIdDescription": "Vaše Azure nájemce ID (nalezeno v přehledu Azure Active Directory – Azure)", - "idpAzureClientIdDescription": "Vaše ID registrace aplikace Azure", - "idpAzureClientSecretDescription": "Tajný klíč registrace aplikace Azure", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", - "idpGoogleConfigurationTitle": "Konfigurace Google", - "idpAzureConfigurationTitle": "Nastavení Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Vaše ID registrace aplikace Azure", - "idpAzureClientSecretDescription2": "Tajný klíč registrace aplikace Azure", - "subnet": "Podsíť", - "subnetDescription": "Podsíť pro konfiguraci sítě této organizace.", - "authPage": "Auth stránka", - "authPageDescription": "Konfigurace autentizační stránky vaší organizace", - "authPageDomain": "Doména ověření stránky", - "noDomainSet": "Není nastavena žádná doména", - "changeDomain": "Změnit doménu", - "selectDomain": "Vybrat doménu", - "restartCertificate": "Restartovat certifikát", - "editAuthPageDomain": "Upravit doménu autentizační stránky", - "setAuthPageDomain": "Nastavit doménu autentické stránky", - "failedToFetchCertificate": "Nepodařilo se načíst certifikát", - "failedToRestartCertificate": "Restartování certifikátu se nezdařilo", - "addDomainToEnableCustomAuthPages": "Přidejte doménu pro povolení vlastních ověřovacích stránek pro vaši organizaci", - "selectDomainForOrgAuthPage": "Vyberte doménu pro ověřovací stránku organizace", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Poskytnutá doména", "domainPickerFreeProvidedDomain": "Zdarma poskytnutá doména", "domainPickerVerified": "Ověřeno", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nemohl být platný pro {domain}.", "domainPickerSubdomainSanitized": "Upravená subdoména", "domainPickerSubdomainCorrected": "\"{sub}\" bylo opraveno na \"{sanitized}\"", - "orgAuthSignInTitle": "Přihlaste se do vaší organizace", - "orgAuthChooseIdpDescription": "Chcete-li pokračovat, vyberte svého poskytovatele identity", - "orgAuthNoIdpConfigured": "Tato organizace nemá nakonfigurovány žádné poskytovatele identity. Místo toho se můžete přihlásit s vaší Pangolinovou identitou.", - "orgAuthSignInWithPangolin": "Přihlásit se pomocí Pangolinu", - "subscriptionRequiredToUse": "Pro použití této funkce je vyžadováno předplatné.", - "idpDisabled": "Poskytovatelé identit jsou zakázáni.", - "orgAuthPageDisabled": "Ověřovací stránka organizace je zakázána.", - "domainRestartedDescription": "Ověření domény bylo úspěšně restartováno", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Upravit soubor: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", - "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde." + "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From ccff0592ca392dba970d6cefd6686f4bd35adfec Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:17 -0700 Subject: [PATCH 131/322] New translations en-us.json (German) --- messages/de-DE.json | 369 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 182 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index f58a0b86..430a7949 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", "siteWg": "Einfacher WireGuard Tunnel", "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", - "siteWgDescriptionSaas": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu erstellen. Manuelles NAT-Setup erforderlich. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", - "siteLocalDescriptionSaas": "Nur lokale Ressourcen. Keine Tunneldurchführung. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Alle Standorte anzeigen", "siteTunnelDescription": "Lege fest, wie du dich mit deinem Standort verbinden möchtest", "siteNewtCredentials": "Neue Newt Zugangsdaten", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-Ressource", "resourceHTTPDescription": "Proxy-Anfragen an Ihre App über HTTPS unter Verwendung einer Subdomain oder einer Basis-Domain.", "resourceRaw": "Rohe TCP/UDP Ressource", - "resourceRawDescription": "Proxy-Anfragen an Ihre App über TCP/UDP mit einer Portnummer.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Ressource erstellen", "resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen", "resourceSeeAll": "Alle Ressourcen anzeigen", @@ -168,9 +168,9 @@ "siteSelect": "Standort auswählen", "siteSearch": "Standorte durchsuchen", "siteNotFound": "Keinen Standort gefunden.", - "selectCountry": "Land auswählen", - "searchCountries": "Länder suchen...", - "noCountryFound": "Kein Land gefunden.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Dieser Standort wird die Verbindung zum Ziel herstellen.", "resourceType": "Ressourcentyp", "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", @@ -1140,8 +1140,8 @@ "sidebarAllUsers": "Alle Benutzer", "sidebarIdentityProviders": "Identitätsanbieter", "sidebarLicense": "Lizenz", - "sidebarClients": "Kunden (Beta)", - "sidebarDomains": "Domänen", + "sidebarClients": "Clients (Beta)", + "sidebarDomains": "Domains", "enableDockerSocket": "Docker Blaupause aktivieren", "enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.", "enableDockerSocketLink": "Mehr erfahren", @@ -1189,7 +1189,7 @@ "certificateStatus": "Zertifikatsstatus", "loading": "Laden", "restart": "Neustart", - "domains": "Domänen", + "domains": "Domains", "domainsDescription": "Domains für Ihre Organisation verwalten", "domainsSearch": "Domains durchsuchen...", "domainAdd": "Domain hinzufügen", @@ -1202,7 +1202,7 @@ "domainMessageConfirm": "Um zu bestätigen, geben Sie bitte den Domainnamen unten ein.", "domainConfirmDelete": "Domain-Löschung bestätigen", "domainDelete": "Domain löschen", - "domain": "Domäne", + "domain": "Domain", "selectDomainTypeNsName": "Domain-Delegation (NS)", "selectDomainTypeNsDescription": "Diese Domain und alle ihre Subdomains. Verwenden Sie dies, wenn Sie eine gesamte Domainzone kontrollieren möchten.", "selectDomainTypeCnameName": "Einzelne Domain (CNAME)", @@ -1242,7 +1242,7 @@ "sidebarExpand": "Erweitern", "newtUpdateAvailable": "Update verfügbar", "newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.", - "domainPickerEnterDomain": "Domäne", + "domainPickerEnterDomain": "Domain", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Geben Sie die vollständige Domäne der Ressource ein, um verfügbare Optionen zu sehen.", "domainPickerDescriptionSaas": "Geben Sie eine vollständige Domäne, Subdomäne oder einfach einen Namen ein, um verfügbare Optionen zu sehen", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdomain: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mehr anzeigen", - "regionSelectorTitle": "Region auswählen", - "regionSelectorInfo": "Das Auswählen einer Region hilft uns, eine bessere Leistung für Ihren Standort bereitzustellen. Sie müssen sich nicht in derselben Region wie Ihr Server befinden.", - "regionSelectorPlaceholder": "Wähle eine Region", - "regionSelectorComingSoon": "Kommt bald", - "billingLoadingSubscription": "Abonnement wird geladen...", - "billingFreeTier": "Kostenlose Stufe", - "billingWarningOverLimit": "Warnung: Sie haben ein oder mehrere Nutzungslimits überschritten. Ihre Webseiten werden nicht verbunden, bis Sie Ihr Abonnement ändern oder Ihren Verbrauch anpassen.", - "billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen", - "billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@fossorial.io.", - "billingDataUsage": "Datenverbrauch", - "billingOnlineTime": "Online-Zeit der Seite", - "billingUsers": "Aktive Benutzer", - "billingDomains": "Aktive Domänen", - "billingRemoteExitNodes": "Aktive selbstgehostete Nodes", - "billingNoLimitConfigured": "Kein Limit konfiguriert", - "billingEstimatedPeriod": "Geschätzter Abrechnungszeitraum", - "billingIncludedUsage": "Inklusive Nutzung", - "billingIncludedUsageDescription": "Nutzung, die in Ihrem aktuellen Abonnementplan enthalten ist", - "billingFreeTierIncludedUsage": "Nutzungskontingente der kostenlosen Stufe", - "billingIncluded": "inbegriffen", - "billingEstimatedTotal": "Geschätzte Gesamtsumme:", - "billingNotes": "Notizen", - "billingEstimateNote": "Dies ist eine Schätzung basierend auf Ihrem aktuellen Verbrauch.", - "billingActualChargesMayVary": "Tatsächliche Kosten können variieren.", - "billingBilledAtEnd": "Sie werden am Ende des Abrechnungszeitraums in Rechnung gestellt.", - "billingModifySubscription": "Abonnement ändern", - "billingStartSubscription": "Abonnement starten", - "billingRecurringCharge": "Wiederkehrende Kosten", - "billingManageSubscriptionSettings": "Verwalten Sie Ihre Abonnement-Einstellungen und Präferenzen", - "billingNoActiveSubscription": "Sie haben kein aktives Abonnement. Starten Sie Ihr Abonnement, um Nutzungslimits zu erhöhen.", - "billingFailedToLoadSubscription": "Fehler beim Laden des Abonnements", - "billingFailedToLoadUsage": "Fehler beim Laden der Nutzung", - "billingFailedToGetCheckoutUrl": "Fehler beim Abrufen der Checkout-URL", - "billingPleaseTryAgainLater": "Bitte versuchen Sie es später noch einmal.", - "billingCheckoutError": "Checkout-Fehler", - "billingFailedToGetPortalUrl": "Fehler beim Abrufen der Portal-URL", - "billingPortalError": "Portalfehler", - "billingDataUsageInfo": "Wenn Sie mit der Cloud verbunden sind, werden alle Daten über Ihre sicheren Tunnel belastet. Dies schließt eingehenden und ausgehenden Datenverkehr über alle Ihre Websites ein. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Daten werden nicht belastet, wenn Sie Knoten verwenden.", - "billingOnlineTimeInfo": "Sie werden belastet, abhängig davon, wie lange Ihre Seiten mit der Cloud verbunden bleiben. Zum Beispiel 44.640 Minuten entspricht einer Site, die 24 Stunden am Tag des Monats läuft. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Die Zeit wird nicht belastet, wenn Sie Knoten verwenden.", - "billingUsersInfo": "Ihnen wird für jeden Benutzer in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Benutzerkonten in Ihrer Organisation.", - "billingDomainInfo": "Ihnen wird für jede Domäne in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Domänenkonten in Ihrer Organisation.", - "billingRemoteExitNodesInfo": "Ihnen wird für jeden verwalteten Node in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven verwalteten Nodes in Ihrer Organisation.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domain nicht gefunden", "domainNotFoundDescription": "Diese Ressource ist deaktiviert, weil die Domain nicht mehr in unserem System existiert. Bitte setzen Sie eine neue Domain für diese Ressource.", "failed": "Fehlgeschlagen", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Es kann einige Zeit dauern, bis DNS-Änderungen im Internet verbreitet werden. Dies kann je nach Ihrem DNS-Provider und den TTL-Einstellungen von einigen Minuten bis zu 48 Stunden dauern.", "resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich", "resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden", - "billingPricingCalculatorLink": "Preisrechner", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Ich stimme den", "termsOfService": "Nutzungsbedingungen zu", @@ -1371,7 +1371,7 @@ "privacyPolicy": "Datenschutzrichtlinie" }, "siteRequired": "Standort ist erforderlich.", - "olmTunnel": "Olm-Tunnel", + "olmTunnel": "Olm Tunnel", "olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung", "errorCreatingClient": "Fehler beim Erstellen des Clients", "clientDefaultsNotFound": "Kundenvorgaben nicht gefunden", @@ -1412,41 +1412,41 @@ "addNewTarget": "Neues Ziel hinzufügen", "targetsList": "Ziel-Liste", "targetErrorDuplicateTargetFound": "Doppeltes Ziel gefunden", - "healthCheckHealthy": "Gesund", - "healthCheckUnhealthy": "Ungesund", - "healthCheckUnknown": "Unbekannt", - "healthCheck": "Gesundheits-Check", - "configureHealthCheck": "Gesundheits-Check konfigurieren", - "configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein", - "enableHealthChecks": "Gesundheits-Checks aktivieren", - "enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.", - "healthScheme": "Methode", - "healthSelectScheme": "Methode auswählen", - "healthCheckPath": "Pfad", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "Der Pfad zum Überprüfen des Gesundheitszustands.", - "healthyIntervalSeconds": "Gesunder Intervall", - "unhealthyIntervalSeconds": "Ungesunder Intervall", - "IntervalSeconds": "Gesunder Intervall", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Zeit ist in Sekunden", - "retryAttempts": "Wiederholungsversuche", - "expectedResponseCodes": "Erwartete Antwortcodes", - "expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Eigene Kopfzeilen", - "customHeadersDescription": "Header neue Zeile getrennt: Header-Name: Wert", - "headersValidationError": "Header müssen im Format Header-Name: Wert sein.", - "saveHealthCheck": "Gesundheits-Check speichern", - "healthCheckSaved": "Gesundheits-Check gespeichert", - "healthCheckSavedDescription": "Die Konfiguration des Gesundheits-Checks wurde erfolgreich gespeichert", - "healthCheckError": "Fehler beim Gesundheits-Check", - "healthCheckErrorDescription": "Beim Speichern der Gesundheits-Check-Konfiguration ist ein Fehler aufgetreten", - "healthCheckPathRequired": "Gesundheits-Check-Pfad ist erforderlich", - "healthCheckMethodRequired": "HTTP-Methode ist erforderlich", - "healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen", - "healthCheckTimeoutMin": "Timeout muss mindestens 1 Sekunde betragen", - "healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP-Methode", "selectHttpMethod": "HTTP-Methode auswählen", "domainPickerSubdomainLabel": "Subdomain", @@ -1460,8 +1460,8 @@ "domainPickerEnterSubdomainToSearch": "Geben Sie eine Subdomain ein, um verfügbare freie Domains zu suchen und auszuwählen.", "domainPickerFreeDomains": "Freie Domains", "domainPickerSearchForAvailableDomains": "Verfügbare Domains suchen", - "domainPickerNotWorkSelfHosted": "Hinweis: Kostenlose bereitgestellte Domains sind derzeit nicht für selbstgehostete Instanzen verfügbar.", - "resourceDomain": "Domäne", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "resourceDomain": "Domain", "resourceEditDomain": "Domain bearbeiten", "siteName": "Site-Name", "proxyPort": "Port", @@ -1543,72 +1543,72 @@ "autoLoginError": "Fehler bei der automatischen Anmeldung", "autoLoginErrorNoRedirectUrl": "Keine Weiterleitungs-URL vom Identitätsanbieter erhalten.", "autoLoginErrorGeneratingUrl": "Fehler beim Generieren der Authentifizierungs-URL.", - "remoteExitNodeManageRemoteExitNodes": "Selbst-Hosted verwalten", - "remoteExitNodeDescription": "Knoten verwalten, um die Netzwerkverbindung zu erweitern", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Knoten suchen...", - "remoteExitNodeAdd": "Knoten hinzufügen", - "remoteExitNodeErrorDelete": "Fehler beim Löschen des Knotens", - "remoteExitNodeQuestionRemove": "Sind Sie sicher, dass Sie den Knoten {selectedNode} aus der Organisation entfernen möchten?", - "remoteExitNodeMessageRemove": "Einmal entfernt, wird der Knoten nicht mehr zugänglich sein.", - "remoteExitNodeMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Knotens unten ein.", - "remoteExitNodeConfirmDelete": "Löschknoten bestätigen", - "remoteExitNodeDelete": "Knoten löschen", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Knoten erstellen", - "description": "Erstellen Sie einen neuen Knoten, um Ihre Netzwerkverbindung zu erweitern", - "viewAllButton": "Alle Knoten anzeigen", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Erstellungsstrategie", - "description": "Wählen Sie diese Option, um Ihren Knoten manuell zu konfigurieren oder neue Zugangsdaten zu generieren.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Node übernehmen", - "description": "Wählen Sie dies, wenn Sie bereits die Anmeldedaten für den Knoten haben." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Schlüssel generieren", - "description": "Wählen Sie dies, wenn Sie neue Schlüssel für den Knoten generieren möchten" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Vorhandenen Node übernehmen", - "description": "Geben Sie die Zugangsdaten des vorhandenen Knotens ein, den Sie übernehmen möchten", - "nodeIdLabel": "Knoten-ID", - "nodeIdDescription": "Die ID des vorhandenen Knotens, den Sie übernehmen möchten", - "secretLabel": "Geheimnis", - "secretDescription": "Der geheime Schlüssel des vorhandenen Knotens", - "submitButton": "Node übernehmen" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Generierte Anmeldedaten", - "description": "Verwenden Sie diese generierten Anmeldeinformationen, um Ihren Knoten zu konfigurieren", - "nodeIdTitle": "Knoten-ID", - "secretTitle": "Geheimnis", - "saveCredentialsTitle": "Anmeldedaten zur Konfiguration hinzufügen", - "saveCredentialsDescription": "Fügen Sie diese Anmeldedaten zu Ihrer selbst-gehosteten Pangolin Node-Konfigurationsdatei hinzu, um die Verbindung abzuschließen.", - "submitButton": "Knoten erstellen" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "Knoten-ID und Geheimnis sind erforderlich, wenn ein existierender Knoten angenommen wird" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Fehler beim Laden der Standardeinstellungen", - "defaultsNotLoaded": "Standardeinstellungen nicht geladen", - "createFailed": "Knoten konnte nicht erstellt werden" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Knoten erfolgreich erstellt" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Knotenauswahl", - "remoteExitNodeSelectionDescription": "Wählen Sie einen Knoten aus, durch den Traffic für diese lokale Seite geleitet werden soll", - "remoteExitNodeRequired": "Ein Knoten muss für lokale Seiten ausgewählt sein", - "noRemoteExitNodesAvailable": "Keine Knoten verfügbar", - "noRemoteExitNodesAvailableDescription": "Für diese Organisation sind keine Knoten verfügbar. Erstellen Sie zuerst einen Knoten, um lokale Sites zu verwenden.", - "exitNode": "Exit-Node", - "country": "Land", - "rulesMatchCountry": "Derzeit basierend auf der Quell-IP", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Verwaltetes Selbsthosted", "description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internationale Domain erkannt", "willbestoredas": "Wird gespeichert als:", - "roleMappingDescription": "Legen Sie fest, wie den Benutzern Rollen zugewiesen werden, wenn sie sich anmelden, wenn Auto Provision aktiviert ist.", - "selectRole": "Wählen Sie eine Rolle", - "roleMappingExpression": "Ausdruck", - "selectRolePlaceholder": "Rolle auswählen", - "selectRoleDescription": "Wählen Sie eine Rolle aus, die allen Benutzern von diesem Identitätsprovider zugewiesen werden soll", - "roleMappingExpressionDescription": "Geben Sie einen JMESPath-Ausdruck ein, um Rolleninformationen aus dem ID-Token zu extrahieren", - "idpTenantIdRequired": "Mandant ID ist erforderlich", - "invalidValue": "Ungültiger Wert", - "idpTypeLabel": "Identitätsanbietertyp", - "roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'", - "idpGoogleConfiguration": "Google-Konfiguration", - "idpGoogleConfigurationDescription": "Konfigurieren Sie Ihre Google OAuth2 Zugangsdaten", - "idpGoogleClientIdDescription": "Ihre Google OAuth2 Client-ID", - "idpGoogleClientSecretDescription": "Ihr Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Konfiguration", - "idpAzureConfigurationDescription": "Konfigurieren Sie Ihre Azure Entra ID OAuth2 Zugangsdaten", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "deine Mandant-ID", - "idpAzureTenantIdDescription": "Ihre Azure Mieter-ID (gefunden in Azure Active Directory Übersicht)", - "idpAzureClientIdDescription": "Ihre Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Ihr Azure App Registration Client Secret", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google-Konfiguration", - "idpAzureConfigurationTitle": "Azure Entra ID Konfiguration", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Ihre Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Ihr Azure App Registration Client Secret", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC Provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnetz", - "subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.", - "authPage": "Auth Seite", - "authPageDescription": "Konfigurieren Sie die Auth-Seite für Ihre Organisation", - "authPageDomain": "Domain der Auth Seite", - "noDomainSet": "Keine Domäne gesetzt", - "changeDomain": "Domain ändern", - "selectDomain": "Domain auswählen", - "restartCertificate": "Zertifikat neu starten", - "editAuthPageDomain": "Auth Page Domain bearbeiten", - "setAuthPageDomain": "Domain der Auth Seite festlegen", - "failedToFetchCertificate": "Zertifikat konnte nicht abgerufen werden", - "failedToRestartCertificate": "Zertifikat konnte nicht neu gestartet werden", - "addDomainToEnableCustomAuthPages": "Fügen Sie eine Domain hinzu, um benutzerdefinierte Authentifizierungsseiten für Ihre Organisation zu aktivieren", - "selectDomainForOrgAuthPage": "Wählen Sie eine Domain für die Authentifizierungsseite der Organisation", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Angegebene Domain", "domainPickerFreeProvidedDomain": "Kostenlose Domain", "domainPickerVerified": "Verifiziert", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" konnte nicht für {domain} gültig gemacht werden.", "domainPickerSubdomainSanitized": "Subdomain bereinigt", "domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"", - "orgAuthSignInTitle": "Bei Ihrer Organisation anmelden", - "orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren", - "orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.", - "orgAuthSignInWithPangolin": "Mit Pangolin anmelden", - "subscriptionRequiredToUse": "Um diese Funktion nutzen zu können, ist ein Abonnement erforderlich.", - "idpDisabled": "Identitätsanbieter sind deaktiviert.", - "orgAuthPageDisabled": "Organisations-Authentifizierungsseite ist deaktiviert.", - "domainRestartedDescription": "Domain-Verifizierung erfolgreich neu gestartet", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", - "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück." + "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 024eb2b15793f151ce52b9d21bbdc1406c5a294d Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:18 -0700 Subject: [PATCH 132/322] New translations en-us.json (Italian) --- messages/it-IT.json | 365 ++++++++++++++++++++++---------------------- 1 file changed, 185 insertions(+), 180 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 10286f7d..93fa58d0 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -36,8 +36,8 @@ "viewSettings": "Visualizza impostazioni", "delete": "Elimina", "name": "Nome", - "online": "In linea", - "offline": "Non in linea", + "online": "Online", + "offline": "Offline", "site": "Sito", "dataIn": "Dati In", "dataOut": "Dati Fuori", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint nella rete. Nessuna configurazione aggiuntiva.", "siteWg": "WireGuard Base", "siteWgDescription": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta.", - "siteWgDescriptionSaas": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta. FUNZIONA SOLO SU NODI AUTO-OSPITATI", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Solo risorse locali. Nessun tunneling.", - "siteLocalDescriptionSaas": "Solo risorse locali. Nessun tunneling. FUNZIONA SOLO SU NODI AUTO-OSPITATI", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Vedi Tutti I Siti", "siteTunnelDescription": "Determina come vuoi connetterti al tuo sito", "siteNewtCredentials": "Credenziali Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Risorsa HTTPS", "resourceHTTPDescription": "Richieste proxy alla tua app tramite HTTPS utilizzando un sottodominio o un dominio di base.", "resourceRaw": "Risorsa Raw TCP/UDP", - "resourceRawDescription": "Richieste proxy alla tua app tramite TCP/UDP utilizzando un numero di porta.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Crea Risorsa", "resourceCreateDescription": "Segui i passaggi seguenti per creare una nuova risorsa", "resourceSeeAll": "Vedi Tutte Le Risorse", @@ -168,9 +168,9 @@ "siteSelect": "Seleziona sito", "siteSearch": "Cerca sito", "siteNotFound": "Nessun sito trovato.", - "selectCountry": "Seleziona paese", - "searchCountries": "Cerca paesi...", - "noCountryFound": "Nessun paese trovato.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Questo sito fornirà connettività all'obiettivo.", "resourceType": "Tipo Di Risorsa", "resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa", @@ -1221,7 +1221,7 @@ "orgBillingDescription": "Gestisci le tue informazioni di fatturazione e abbonamenti", "github": "GitHub", "pangolinHosted": "Pangolin Hosted", - "fossorial": "Fossoriale", + "fossorial": "Fossorial", "completeAccountSetup": "Completa la Configurazione dell'Account", "completeAccountSetupDescription": "Imposta la tua password per iniziare", "accountSetupSent": "Invieremo un codice di configurazione dell'account a questo indirizzo email.", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Sottodominio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostra Altro", - "regionSelectorTitle": "Seleziona regione", - "regionSelectorInfo": "Selezionare una regione ci aiuta a fornire migliori performance per la tua posizione. Non devi necessariamente essere nella stessa regione del tuo server.", - "regionSelectorPlaceholder": "Scegli una regione", - "regionSelectorComingSoon": "Prossimamente", - "billingLoadingSubscription": "Caricamento abbonamento...", - "billingFreeTier": "Piano Gratuito", - "billingWarningOverLimit": "Avviso: Hai superato uno o più limiti di utilizzo. I tuoi siti non si connetteranno finché non modifichi il tuo abbonamento o non adegui il tuo utilizzo.", - "billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo", - "billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@fossorial.io.", - "billingDataUsage": "Utilizzo dei Dati", - "billingOnlineTime": "Tempo Online del Sito", - "billingUsers": "Utenti Attivi", - "billingDomains": "Domini Attivi", - "billingRemoteExitNodes": "Nodi Self-hosted Attivi", - "billingNoLimitConfigured": "Nessun limite configurato", - "billingEstimatedPeriod": "Periodo di Fatturazione Stimato", - "billingIncludedUsage": "Utilizzo Incluso", - "billingIncludedUsageDescription": "Utilizzo incluso nel tuo piano di abbonamento corrente", - "billingFreeTierIncludedUsage": "Elenchi di utilizzi inclusi nel piano gratuito", - "billingIncluded": "incluso", - "billingEstimatedTotal": "Totale Stimato:", - "billingNotes": "Note", - "billingEstimateNote": "Questa è una stima basata sul tuo utilizzo attuale.", - "billingActualChargesMayVary": "I costi effettivi possono variare.", - "billingBilledAtEnd": "Sarai fatturato alla fine del periodo di fatturazione.", - "billingModifySubscription": "Modifica Abbonamento", - "billingStartSubscription": "Inizia Abbonamento", - "billingRecurringCharge": "Addebito Ricorrente", - "billingManageSubscriptionSettings": "Gestisci impostazioni e preferenze dell'abbonamento", - "billingNoActiveSubscription": "Non hai un abbonamento attivo. Avvia il tuo abbonamento per aumentare i limiti di utilizzo.", - "billingFailedToLoadSubscription": "Caricamento abbonamento fallito", - "billingFailedToLoadUsage": "Caricamento utilizzo fallito", - "billingFailedToGetCheckoutUrl": "Errore durante l'ottenimento dell'URL di pagamento", - "billingPleaseTryAgainLater": "Per favore, riprova più tardi.", - "billingCheckoutError": "Errore di Pagamento", - "billingFailedToGetPortalUrl": "Errore durante l'ottenimento dell'URL del portale", - "billingPortalError": "Errore del Portale", - "billingDataUsageInfo": "Hai addebitato tutti i dati trasferiti attraverso i tunnel sicuri quando sei connesso al cloud. Questo include sia il traffico in entrata e in uscita attraverso tutti i siti. Quando si raggiunge il limite, i siti si disconnetteranno fino a quando non si aggiorna il piano o si riduce l'utilizzo. I dati non vengono caricati quando si utilizzano nodi.", - "billingOnlineTimeInfo": "Ti viene addebitato in base al tempo in cui i tuoi siti rimangono connessi al cloud. Ad esempio, 44,640 minuti è uguale a un sito in esecuzione 24/7 per un mese intero. Quando raggiungi il tuo limite, i tuoi siti si disconnetteranno fino a quando non aggiorni il tuo piano o riduci l'utilizzo. Il tempo non viene caricato quando si usano i nodi.", - "billingUsersInfo": "Sei addebitato per ogni utente nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account utente attivi nella tua organizzazione.", - "billingDomainInfo": "Sei addebitato per ogni dominio nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account dominio attivi nella tua organizzazione.", - "billingRemoteExitNodesInfo": "Sei addebitato per ogni nodo gestito nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di nodi gestiti attivi nella tua organizzazione.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domini Non Trovati", "domainNotFoundDescription": "Questa risorsa è disabilitata perché il dominio non esiste più nel nostro sistema. Si prega di impostare un nuovo dominio per questa risorsa.", "failed": "Fallito", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Le modifiche DNS possono richiedere del tempo per propagarsi in Internet. Questo può richiedere da pochi minuti a 48 ore, a seconda del tuo provider DNS e delle impostazioni TTL.", "resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP", "resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP", - "billingPricingCalculatorLink": "Calcolatore di Prezzi", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Accetto i", "termsOfService": "termini di servizio", @@ -1371,7 +1371,7 @@ "privacyPolicy": "informativa sulla privacy" }, "siteRequired": "Il sito è richiesto.", - "olmTunnel": "Tunnel Olm", + "olmTunnel": "Olm Tunnel", "olmTunnelDescription": "Usa Olm per la connettività client", "errorCreatingClient": "Errore nella creazione del client", "clientDefaultsNotFound": "Impostazioni predefinite del client non trovate", @@ -1412,41 +1412,41 @@ "addNewTarget": "Aggiungi Nuovo Target", "targetsList": "Elenco dei Target", "targetErrorDuplicateTargetFound": "Target duplicato trovato", - "healthCheckHealthy": "Sano", - "healthCheckUnhealthy": "Non Sano", - "healthCheckUnknown": "Sconosciuto", - "healthCheck": "Controllo Salute", - "configureHealthCheck": "Configura Controllo Salute", - "configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}", - "enableHealthChecks": "Abilita i Controlli di Salute", - "enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.", - "healthScheme": "Metodo", - "healthSelectScheme": "Seleziona Metodo", - "healthCheckPath": "Percorso", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", - "healthPort": "Porta", - "healthCheckPathDescription": "Percorso per verificare lo stato di salute.", - "healthyIntervalSeconds": "Intervallo Sano", - "unhealthyIntervalSeconds": "Intervallo Non Sano", - "IntervalSeconds": "Intervallo Sano", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Il tempo è in secondi", - "retryAttempts": "Tentativi di Riprova", - "expectedResponseCodes": "Codici di Risposta Attesi", - "expectedResponseCodesDescription": "Codice di stato HTTP che indica lo stato di salute. Se lasciato vuoto, considerato sano è compreso tra 200-300.", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Intestazioni Personalizzate", - "customHeadersDescription": "Intestazioni nuova riga separate: Intestazione-Nome: valore", - "headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.", - "saveHealthCheck": "Salva Controllo Salute", - "healthCheckSaved": "Controllo Salute Salvato", - "healthCheckSavedDescription": "La configurazione del controllo salute è stata salvata con successo", - "healthCheckError": "Errore Controllo Salute", - "healthCheckErrorDescription": "Si è verificato un errore durante il salvataggio della configurazione del controllo salute.", - "healthCheckPathRequired": "Il percorso del controllo salute è richiesto", - "healthCheckMethodRequired": "Metodo HTTP richiesto", - "healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi", - "healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo", - "healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "Metodo HTTP", "selectHttpMethod": "Seleziona metodo HTTP", "domainPickerSubdomainLabel": "Sottodominio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Inserisci un sottodominio per cercare e selezionare dai domini gratuiti disponibili.", "domainPickerFreeDomains": "Domini Gratuiti", "domainPickerSearchForAvailableDomains": "Cerca domini disponibili", - "domainPickerNotWorkSelfHosted": "Nota: I domini forniti gratuitamente non sono disponibili per le istanze self-hosted al momento.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Dominio", "resourceEditDomain": "Modifica Dominio", "siteName": "Nome del Sito", @@ -1543,72 +1543,72 @@ "autoLoginError": "Errore di Accesso Automatico", "autoLoginErrorNoRedirectUrl": "Nessun URL di reindirizzamento ricevuto dal provider di identità.", "autoLoginErrorGeneratingUrl": "Impossibile generare l'URL di autenticazione.", - "remoteExitNodeManageRemoteExitNodes": "Gestisci Self-Hosted", - "remoteExitNodeDescription": "Gestisci i nodi per estendere la connettività di rete", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Cerca nodi...", - "remoteExitNodeAdd": "Aggiungi Nodo", - "remoteExitNodeErrorDelete": "Errore nell'eliminare il nodo", - "remoteExitNodeQuestionRemove": "Sei sicuro di voler rimuovere il nodo {selectedNode} dall'organizzazione?", - "remoteExitNodeMessageRemove": "Una volta rimosso, il nodo non sarà più accessibile.", - "remoteExitNodeMessageConfirm": "Per confermare, digita il nome del nodo qui sotto.", - "remoteExitNodeConfirmDelete": "Conferma Eliminazione Nodo", - "remoteExitNodeDelete": "Elimina Nodo", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Crea Nodo", - "description": "Crea un nuovo nodo per estendere la connettività di rete", - "viewAllButton": "Visualizza Tutti I Nodi", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Strategia di Creazione", - "description": "Scegli questa opzione per configurare manualmente il nodo o generare nuove credenziali.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adotta Nodo", - "description": "Scegli questo se hai già le credenziali per il nodo." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Genera Chiavi", - "description": "Scegli questa opzione se vuoi generare nuove chiavi per il nodo" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adotta Nodo Esistente", - "description": "Inserisci le credenziali del nodo esistente che vuoi adottare", - "nodeIdLabel": "ID Nodo", - "nodeIdDescription": "L'ID del nodo esistente che si desidera adottare", - "secretLabel": "Segreto", - "secretDescription": "La chiave segreta del nodo esistente", - "submitButton": "Adotta Nodo" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Credenziali Generate", - "description": "Usa queste credenziali generate per configurare il nodo", - "nodeIdTitle": "ID Nodo", - "secretTitle": "Segreto", - "saveCredentialsTitle": "Aggiungi Credenziali alla Configurazione", - "saveCredentialsDescription": "Aggiungi queste credenziali al tuo file di configurazione del nodo self-hosted Pangolin per completare la connessione.", - "submitButton": "Crea Nodo" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "L'ID del nodo e il segreto sono necessari quando si adotta un nodo esistente" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Caricamento impostazioni predefinite fallito", - "defaultsNotLoaded": "Impostazioni predefinite non caricate", - "createFailed": "Impossibile creare il nodo" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Nodo creato con successo" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Selezione Nodo", - "remoteExitNodeSelectionDescription": "Seleziona un nodo per instradare il traffico per questo sito locale", - "remoteExitNodeRequired": "Un nodo deve essere selezionato per i siti locali", - "noRemoteExitNodesAvailable": "Nessun Nodo Disponibile", - "noRemoteExitNodesAvailableDescription": "Non ci sono nodi disponibili per questa organizzazione. Crea un nodo prima per usare i siti locali.", - "exitNode": "Nodo di Uscita", - "country": "Paese", - "rulesMatchCountry": "Attualmente basato sull'IP di origine", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Gestito Auto-Ospitato", "description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internazionale Rilevato", "willbestoredas": "Verrà conservato come:", - "roleMappingDescription": "Determinare come i ruoli sono assegnati agli utenti quando accedono quando è abilitata la fornitura automatica.", - "selectRole": "Seleziona un ruolo", - "roleMappingExpression": "Espressione", - "selectRolePlaceholder": "Scegli un ruolo", - "selectRoleDescription": "Seleziona un ruolo da assegnare a tutti gli utenti da questo provider di identità", - "roleMappingExpressionDescription": "Inserire un'espressione JMESPath per estrarre le informazioni sul ruolo dal token ID", - "idpTenantIdRequired": "L'ID dell'inquilino è obbligatorio", - "invalidValue": "Valore non valido", - "idpTypeLabel": "Tipo Provider Identità", - "roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' 'Membro'", - "idpGoogleConfiguration": "Configurazione Google", - "idpGoogleConfigurationDescription": "Configura le tue credenziali di Google OAuth2", - "idpGoogleClientIdDescription": "Il Tuo Client Id Google OAuth2", - "idpGoogleClientSecretDescription": "Il Tuo Client Google OAuth2 Secret", - "idpAzureConfiguration": "Configurazione Azure Entra ID", - "idpAzureConfigurationDescription": "Configura le credenziali OAuth2 di Azure Entra ID", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "iltuo-inquilino-id", - "idpAzureTenantIdDescription": "Il tuo ID del tenant Azure (trovato nella panoramica di Azure Active Directory)", - "idpAzureClientIdDescription": "Il Tuo Id Client Registrazione App Azure", - "idpAzureClientSecretDescription": "Il Tuo Client Di Registrazione App Azure Secret", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Configurazione Google", - "idpAzureConfigurationTitle": "Configurazione Azure Entra ID", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Il Tuo Id Client Registrazione App Azure", - "idpAzureClientSecretDescription2": "Il Tuo Client Di Registrazione App Azure Secret", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Sottorete", - "subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.", - "authPage": "Pagina Autenticazione", - "authPageDescription": "Configura la pagina di autenticazione per la tua organizzazione", - "authPageDomain": "Dominio Pagina Auth", - "noDomainSet": "Nessun dominio impostato", - "changeDomain": "Cambia Dominio", - "selectDomain": "Seleziona Dominio", - "restartCertificate": "Riavvia Certificato", - "editAuthPageDomain": "Modifica Dominio Pagina Auth", - "setAuthPageDomain": "Imposta Dominio Pagina Autenticazione", - "failedToFetchCertificate": "Recupero del certificato non riuscito", - "failedToRestartCertificate": "Riavvio del certificato non riuscito", - "addDomainToEnableCustomAuthPages": "Aggiungi un dominio per abilitare le pagine di autenticazione personalizzate per la tua organizzazione", - "selectDomainForOrgAuthPage": "Seleziona un dominio per la pagina di autenticazione dell'organizzazione", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Dominio Fornito", "domainPickerFreeProvidedDomain": "Dominio Fornito Gratuito", "domainPickerVerified": "Verificato", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" non può essere reso valido per {domain}.", "domainPickerSubdomainSanitized": "Sottodominio igienizzato", "domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"", - "orgAuthSignInTitle": "Accedi alla tua organizzazione", - "orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare", - "orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.", - "orgAuthSignInWithPangolin": "Accedi con Pangolino", - "subscriptionRequiredToUse": "Per utilizzare questa funzionalità è necessario un abbonamento.", - "idpDisabled": "I provider di identità sono disabilitati.", - "orgAuthPageDisabled": "La pagina di autenticazione dell'organizzazione è disabilitata.", - "domainRestartedDescription": "Verifica del dominio riavviata con successo", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", - "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui." + "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From e73e6956a54d74f4b351cc9685a467dc2b89664b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:19 -0700 Subject: [PATCH 133/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 367 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 181 deletions(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 4684eacb..34888669 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "네트워크에 대한 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.", "siteWg": "기본 WireGuard", "siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.", - "siteWgDescriptionSaas": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다. 자체 호스팅 노드에서만 작동합니다.", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "로컬 리소스만 사용 가능합니다. 터널링이 없습니다.", - "siteLocalDescriptionSaas": "로컬 리소스만. 터널링 없음. 자체 호스팅 노드에서만 작동합니다.", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "모든 사이트 보기", "siteTunnelDescription": "사이트에 연결하는 방법을 결정하세요", "siteNewtCredentials": "Newt 자격 증명", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS 리소스", "resourceHTTPDescription": "서브도메인 또는 기본 도메인을 사용하여 HTTPS를 통해 앱에 대한 요청을 프록시합니다.", "resourceRaw": "원시 TCP/UDP 리소스", - "resourceRawDescription": "TCP/UDP를 통해 포트 번호를 사용하여 앱에 요청을 프록시합니다.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "리소스 생성", "resourceCreateDescription": "아래 단계를 따라 새 리소스를 생성하세요.", "resourceSeeAll": "모든 리소스 보기", @@ -168,9 +168,9 @@ "siteSelect": "사이트 선택", "siteSearch": "사이트 검색", "siteNotFound": "사이트를 찾을 수 없습니다.", - "selectCountry": "국가 선택하기", - "searchCountries": "국가 검색...", - "noCountryFound": "국가를 찾을 수 없습니다.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "이 사이트는 대상에 대한 연결을 제공합니다.", "resourceType": "리소스 유형", "resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "서브도메인: {subdomain}", "domainPickerNamespace": "이름 공간: {namespace}", "domainPickerShowMore": "더보기", - "regionSelectorTitle": "지역 선택", - "regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.", - "regionSelectorPlaceholder": "지역 선택", - "regionSelectorComingSoon": "곧 출시 예정", - "billingLoadingSubscription": "구독 불러오는 중...", - "billingFreeTier": "무료 티어", - "billingWarningOverLimit": "경고: 하나 이상의 사용 한도를 초과했습니다. 구독을 수정하거나 사용량을 조정하기 전까지 사이트는 연결되지 않습니다.", - "billingUsageLimitsOverview": "사용 한도 개요", - "billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@fossorial.io로 연락하십시오.", - "billingDataUsage": "데이터 사용량", - "billingOnlineTime": "사이트 온라인 시간", - "billingUsers": "활성 사용자", - "billingDomains": "활성 도메인", - "billingRemoteExitNodes": "활성 자체 호스팅 노드", - "billingNoLimitConfigured": "구성된 한도가 없습니다.", - "billingEstimatedPeriod": "예상 청구 기간", - "billingIncludedUsage": "포함 사용량", - "billingIncludedUsageDescription": "현재 구독 계획에 포함된 사용량", - "billingFreeTierIncludedUsage": "무료 티어 사용 허용량", - "billingIncluded": "포함됨", - "billingEstimatedTotal": "예상 총액:", - "billingNotes": "노트", - "billingEstimateNote": "현재 사용량을 기반으로 한 추정치입니다.", - "billingActualChargesMayVary": "실제 청구 금액은 다를 수 있습니다.", - "billingBilledAtEnd": "청구 기간이 끝난 후 청구됩니다.", - "billingModifySubscription": "구독 수정", - "billingStartSubscription": "구독 시작", - "billingRecurringCharge": "반복 요금", - "billingManageSubscriptionSettings": "구독 설정 및 기본 설정을 관리합니다", - "billingNoActiveSubscription": "활성 구독이 없습니다. 사용 한도를 늘리려면 구독을 시작하십시오.", - "billingFailedToLoadSubscription": "구독을 불러오는 데 실패했습니다.", - "billingFailedToLoadUsage": "사용량을 불러오는 데 실패했습니다.", - "billingFailedToGetCheckoutUrl": "체크아웃 URL을 가져오는 데 실패했습니다.", - "billingPleaseTryAgainLater": "나중에 다시 시도하십시오.", - "billingCheckoutError": "체크아웃 오류", - "billingFailedToGetPortalUrl": "포털 URL을 가져오는 데 실패했습니다.", - "billingPortalError": "포털 오류", - "billingDataUsageInfo": "클라우드에 연결할 때 보안 터널을 통해 전송된 모든 데이터에 대해 비용이 청구됩니다. 여기에는 모든 사이트의 들어오고 나가는 트래픽이 포함됩니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용하는 경우 데이터는 요금이 청구되지 않습니다.", - "billingOnlineTimeInfo": "사이트가 클라우드에 연결된 시간에 따라 요금이 청구됩니다. 예를 들어, 44,640분은 사이트가 한 달 내내 24시간 작동하는 것과 같습니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용할 때 시간은 요금이 청구되지 않습니다.", - "billingUsersInfo": "조직의 사용자마다 요금이 청구됩니다. 청구는 조직의 활성 사용자 계정 수에 따라 매일 계산됩니다.", - "billingDomainInfo": "조직의 도메인마다 요금이 청구됩니다. 청구는 조직의 활성 도메인 계정 수에 따라 매일 계산됩니다.", - "billingRemoteExitNodesInfo": "조직의 관리 노드마다 요금이 청구됩니다. 청구는 조직의 활성 관리 노드 수에 따라 매일 계산됩니다.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "도메인을 찾을 수 없습니다", "domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.", "failed": "실패", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 변경 사항은 인터넷 전체에 전파되는 데 시간이 걸립니다. DNS 제공자와 TTL 설정에 따라 몇 분에서 48시간까지 걸릴 수 있습니다.", "resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다", "resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요", - "billingPricingCalculatorLink": "가격 계산기", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "동의합니다", "termsOfService": "서비스 약관", @@ -1412,41 +1412,41 @@ "addNewTarget": "새 대상 추가", "targetsList": "대상 목록", "targetErrorDuplicateTargetFound": "중복 대상 발견", - "healthCheckHealthy": "정상", - "healthCheckUnhealthy": "비정상", - "healthCheckUnknown": "알 수 없음", - "healthCheck": "상태 확인", - "configureHealthCheck": "상태 확인 설정", - "configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정", - "enableHealthChecks": "상태 확인 활성화", - "enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.", - "healthScheme": "방법", - "healthSelectScheme": "방법 선택", - "healthCheckPath": "경로", - "healthHostname": "IP / 호스트", - "healthPort": "포트", - "healthCheckPathDescription": "상태 확인을 위한 경로입니다.", - "healthyIntervalSeconds": "정상 간격", - "unhealthyIntervalSeconds": "비정상 간격", - "IntervalSeconds": "정상 간격", - "timeoutSeconds": "시간 초과", - "timeIsInSeconds": "시간은 초 단위입니다", - "retryAttempts": "재시도 횟수", - "expectedResponseCodes": "예상 응답 코드", - "expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "사용자 정의 헤더", - "customHeadersDescription": "헤더는 새 줄로 구분됨: Header-Name: value", - "headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.", - "saveHealthCheck": "상태 확인 저장", - "healthCheckSaved": "상태 확인이 저장되었습니다.", - "healthCheckSavedDescription": "상태 확인 구성이 성공적으로 저장되었습니다", - "healthCheckError": "상태 확인 오류", - "healthCheckErrorDescription": "상태 확인 구성을 저장하는 동안 오류가 발생했습니다", - "healthCheckPathRequired": "상태 확인 경로는 필수입니다.", - "healthCheckMethodRequired": "HTTP 방법은 필수입니다.", - "healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.", - "healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.", - "healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP 메소드", "selectHttpMethod": "HTTP 메소드 선택", "domainPickerSubdomainLabel": "서브도메인", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "사용 가능한 무료 도메인에서 검색 및 선택할 서브도메인 입력.", "domainPickerFreeDomains": "무료 도메인", "domainPickerSearchForAvailableDomains": "사용 가능한 도메인 검색", - "domainPickerNotWorkSelfHosted": "참고: 무료 제공 도메인은 현재 자체 호스팅 인스턴스에 사용할 수 없습니다.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "도메인", "resourceEditDomain": "도메인 수정", "siteName": "사이트 이름", @@ -1543,72 +1543,72 @@ "autoLoginError": "자동 로그인 오류", "autoLoginErrorNoRedirectUrl": "ID 공급자로부터 리디렉션 URL을 받지 못했습니다.", "autoLoginErrorGeneratingUrl": "인증 URL 생성 실패.", - "remoteExitNodeManageRemoteExitNodes": "관리 자체 호스팅", - "remoteExitNodeDescription": "네트워크 연결성을 확장하기 위해 노드를 관리하세요", - "remoteExitNodes": "노드", - "searchRemoteExitNodes": "노드 검색...", - "remoteExitNodeAdd": "노드 추가", - "remoteExitNodeErrorDelete": "노드 삭제 오류", - "remoteExitNodeQuestionRemove": "조직에서 노드 {selectedNode}를 제거하시겠습니까?", - "remoteExitNodeMessageRemove": "한 번 제거되면 더 이상 노드에 접근할 수 없습니다.", - "remoteExitNodeMessageConfirm": "확인을 위해 아래에 노드 이름을 입력해 주세요.", - "remoteExitNodeConfirmDelete": "노드 삭제 확인", - "remoteExitNodeDelete": "노드 삭제", - "sidebarRemoteExitNodes": "노드", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", + "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "노드 생성", - "description": "네트워크 연결성을 확장하기 위해 새 노드를 생성하세요", - "viewAllButton": "모든 노드 보기", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "생성 전략", - "description": "노드를 직접 구성하거나 새 자격 증명을 생성하려면 이것을 선택하세요.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "노드 채택", - "description": "이미 노드의 자격 증명이 있는 경우 이것을 선택하세요." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "키 생성", - "description": "노드에 대한 새 키를 생성하려면 이것을 선택하세요" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "기존 노드 채택", - "description": "채택하려는 기존 노드의 자격 증명을 입력하세요", - "nodeIdLabel": "노드 ID", - "nodeIdDescription": "채택하려는 기존 노드의 ID", - "secretLabel": "비밀", - "secretDescription": "기존 노드의 비밀 키", - "submitButton": "노드 채택" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "생성된 자격 증명", - "description": "생성된 자격 증명을 사용하여 노드를 구성하세요", - "nodeIdTitle": "노드 ID", - "secretTitle": "비밀", - "saveCredentialsTitle": "구성에 자격 증명 추가", - "saveCredentialsDescription": "연결을 완료하려면 이러한 자격 증명을 자체 호스팅 Pangolin 노드 구성 파일에 추가하십시오.", - "submitButton": "노드 생성" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "기존 노드를 채택하려면 노드 ID와 비밀 키가 필요합니다" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "기본값 로드 실패", - "defaultsNotLoaded": "기본값 로드되지 않음", - "createFailed": "노드 생성 실패" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "노드가 성공적으로 생성되었습니다" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "노드 선택", - "remoteExitNodeSelectionDescription": "이 로컬 사이트에서 트래픽을 라우팅할 노드를 선택하세요", - "remoteExitNodeRequired": "로컬 사이트에 노드를 선택해야 합니다", - "noRemoteExitNodesAvailable": "사용 가능한 노드가 없습니다", - "noRemoteExitNodesAvailableDescription": "이 조직에 사용 가능한 노드가 없습니다. 로컬 사이트를 사용하려면 먼저 노드를 생성하세요.", - "exitNode": "종단 노드", - "country": "국가", - "rulesMatchCountry": "현재 소스 IP를 기반으로 합니다", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "관리 자체 호스팅", "description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "국제 도메인 감지됨", "willbestoredas": "다음으로 저장됩니다:", - "roleMappingDescription": "자동 프로비저닝이 활성화되면 사용자가 로그인할 때 역할이 할당되는 방법을 결정합니다.", - "selectRole": "역할 선택", - "roleMappingExpression": "표현식", - "selectRolePlaceholder": "역할 선택", - "selectRoleDescription": "이 신원 공급자로부터 모든 사용자에게 할당할 역할을 선택하십시오.", - "roleMappingExpressionDescription": "ID 토큰에서 역할 정보를 추출하기 위한 JMESPath 표현식을 입력하세요.", - "idpTenantIdRequired": "테넌트 ID가 필요합니다", - "invalidValue": "잘못된 값", - "idpTypeLabel": "신원 공급자 유형", - "roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google 구성", - "idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.", - "idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID", - "idpGoogleClientSecretDescription": "Google OAuth2 클라이언트 비밀", - "idpAzureConfiguration": "Azure Entra ID 구성", - "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 자격 증명을 구성합니다.", - "idpTenantId": "테넌트 ID", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", + "idpTenantId": "Tenant ID", "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Azure 액티브 디렉터리 개요에서 찾은 Azure 테넌트 ID", - "idpAzureClientIdDescription": "Azure 앱 등록 클라이언트 ID", - "idpAzureClientSecretDescription": "Azure 앱 등록 클라이언트 비밀", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "구글", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "애저", - "idpGoogleConfigurationTitle": "Google 구성", - "idpAzureConfigurationTitle": "Azure Entra ID 구성", - "idpTenantIdLabel": "테넌트 ID", - "idpAzureClientIdDescription2": "Azure 앱 등록 클라이언트 ID", - "idpAzureClientSecretDescription2": "Azure 앱 등록 클라이언트 비밀", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC 공급자", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자", - "subnet": "서브넷", - "subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.", - "authPage": "인증 페이지", - "authPageDescription": "조직에 대한 인증 페이지를 구성합니다.", - "authPageDomain": "인증 페이지 도메인", - "noDomainSet": "도메인 설정 없음", - "changeDomain": "도메인 변경", - "selectDomain": "도메인 선택", - "restartCertificate": "인증서 재시작", - "editAuthPageDomain": "인증 페이지 도메인 편집", - "setAuthPageDomain": "인증 페이지 도메인 설정", - "failedToFetchCertificate": "인증서 가져오기 실패", - "failedToRestartCertificate": "인증서 재시작 실패", - "addDomainToEnableCustomAuthPages": "조직의 맞춤 인증 페이지를 활성화하려면 도메인을 추가하세요.", - "selectDomainForOrgAuthPage": "조직 인증 페이지에 대한 도메인을 선택하세요.", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "제공된 도메인", "domainPickerFreeProvidedDomain": "무료 제공된 도메인", "domainPickerVerified": "검증됨", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\"을(를) {domain}에 대해 유효하게 만들 수 없습니다.", "domainPickerSubdomainSanitized": "하위 도메인 정리됨", "domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다", - "orgAuthSignInTitle": "조직에 로그인", - "orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.", - "orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.", - "orgAuthSignInWithPangolin": "Pangolin으로 로그인", - "subscriptionRequiredToUse": "이 기능을 사용하려면 구독이 필요합니다.", - "idpDisabled": "신원 공급자가 비활성화되었습니다.", - "orgAuthPageDisabled": "조직 인증 페이지가 비활성화되었습니다.", - "domainRestartedDescription": "도메인 인증이 성공적으로 재시작되었습니다.", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", - "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요." + "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 3385a92b0f5f547a317d8886576d1774b1a02312 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:20 -0700 Subject: [PATCH 134/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 443 ++++++++++++++++++++++---------------------- 1 file changed, 224 insertions(+), 219 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 12744579..c8047e5f 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -10,7 +10,7 @@ "setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.", "componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.", "componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.", - "welcome": "Welkom bij Pangolin", + "welcome": "Welkom bij Pangolin!", "welcomeTo": "Welkom bij", "componentsCreateOrg": "Maak een Organisatie", "componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.", @@ -22,7 +22,7 @@ "inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.", "inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.", "inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.", - "inviteCreateUser": "U moet eerst een account aanmaken", + "inviteCreateUser": "U moet eerst een account aanmaken.", "goHome": "Ga naar huis", "inviteLogInOtherUser": "Log in als een andere gebruiker", "createAnAccount": "Account aanmaken", @@ -38,12 +38,12 @@ "name": "Naam", "online": "Online", "offline": "Offline", - "site": "Website", - "dataIn": "Gegevens in", - "dataOut": "Data Uit", + "site": "Referentie", + "dataIn": "Dataverbruik inkomend", + "dataOut": "Dataverbruik uitgaand", "connectionType": "Type verbinding", "tunnelType": "Tunnel type", - "local": "lokaal", + "local": "Lokaal", "edit": "Bewerken", "siteConfirmDelete": "Verwijderen van site bevestigen", "siteDelete": "Site verwijderen", @@ -55,7 +55,7 @@ "siteCreate": "Site maken", "siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden", "siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen", - "close": "Afsluiten", + "close": "Sluiten", "siteErrorCreate": "Fout bij maken site", "siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden", "siteErrorCreateDefaults": "Standaardinstellingen niet gevonden", @@ -90,21 +90,21 @@ "siteGeneralDescription": "Algemene instellingen voor deze site configureren", "siteSettingDescription": "Configureer de instellingen op uw site", "siteSetting": "{siteName} instellingen", - "siteNewtTunnel": "Nieuwstunnel (Aanbevolen)", + "siteNewtTunnel": "Newttunnel (Aanbevolen)", "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", - "siteWgDescriptionSaas": "Gebruik elke WireGuard-client om een tunnel op te zetten. Handmatige NAT-instelling vereist. WERKT ALLEEN OP SELF HOSTED NODES", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", - "siteLocalDescriptionSaas": "Alleen lokale bronnen. Geen tunneling. WERKT ALLEEN OP SELF HOSTED NODES", - "siteSeeAll": "Alle werkruimtes bekijken", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteSeeAll": "Alle sites bekijken", "siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site", "siteNewtCredentials": "Nieuwste aanmeldgegevens", "siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server", "siteCredentialsSave": "Uw referenties opslaan", "siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "siteInfo": "Site informatie", - "status": "status", + "status": "Status", "shareTitle": "Beheer deellinks", "shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen", "shareSearch": "Zoek share links...", @@ -146,20 +146,20 @@ "never": "Nooit", "shareErrorSelectResource": "Selecteer een bron", "resourceTitle": "Bronnen beheren", - "resourceDescription": "Veilige proxy's voor uw privé applicaties maken", + "resourceDescription": "Veilige proxy's voor uw privéapplicaties maken", "resourcesSearch": "Zoek bronnen...", "resourceAdd": "Bron toevoegen", "resourceErrorDelte": "Fout bij verwijderen document", "authentication": "Authenticatie", "protected": "Beschermd", - "notProtected": "Niet beschermd", + "notProtected": "Niet beveiligd", "resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.", "resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.", "resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?", "resourceHTTP": "HTTPS bron", "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", - "resourceRaw": "Ruwe TCP/UDP bron", - "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", + "resourceRaw": "TCP/UDP bron", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Bron maken", "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", "resourceSeeAll": "Alle bronnen bekijken", @@ -168,9 +168,9 @@ "siteSelect": "Selecteer site", "siteSearch": "Zoek site", "siteNotFound": "Geen site gevonden.", - "selectCountry": "Selecteer land", - "searchCountries": "Zoek landen...", - "noCountryFound": "Geen land gevonden.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Deze site zal connectiviteit met het doelwit bieden.", "resourceType": "Type bron", "resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron", @@ -186,7 +186,7 @@ "protocolSelect": "Selecteer een protocol", "resourcePortNumber": "Nummer van poort", "resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.", - "cancel": "annuleren", + "cancel": "Annuleren", "resourceConfig": "Configuratie tekstbouwstenen", "resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen", "resourceAddEntrypoints": "Traefik: Entrypoints toevoegen", @@ -215,7 +215,7 @@ "saveGeneralSettings": "Algemene instellingen opslaan", "saveSettings": "Instellingen opslaan", "orgDangerZone": "Gevaarlijke zone", - "orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.", + "orgDangerZoneDescription": "Deze instantie verwijderen is onomkeerbaar. Bevestig alstublieft dat u wilt doorgaan.", "orgDelete": "Verwijder organisatie", "orgDeleteConfirm": "Bevestig Verwijderen Organisatie", "orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.", @@ -268,7 +268,7 @@ "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", "apiKeysList": "Uw API-sleutel", "apiKeysSave": "Uw API-sleutel opslaan", - "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", "apiKeysInfo": "Uw API-sleutel is:", "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", "generate": "Genereren", @@ -504,7 +504,7 @@ "targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.", "methodSelect": "Selecteer methode", "targetSubmit": "Doelwit toevoegen", - "targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.", + "targetNoOne": "Geen doel toegevoegd. Voeg deze toe via dit formulier.", "targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.", "targetsSubmit": "Doelstellingen opslaan", "proxyAdditional": "Extra Proxy-instellingen", @@ -575,7 +575,7 @@ "domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen", "none": "geen", "unknown": "onbekend", - "resources": "Hulpmiddelen", + "resources": "Bronnen", "resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.", "resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling", "resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren", @@ -601,7 +601,7 @@ "newtId": "Newt-ID", "newtSecretKey": "Nieuwe geheime sleutel", "architecture": "Architectuur", - "sites": "Werkruimtes", + "sites": "Sites", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.", "siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients", "siteWgManualConfigurationRequired": "Handmatige configuratie vereist", @@ -730,22 +730,22 @@ "idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?", "idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.", "idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.", - "idpConfirmDelete": "Bevestig verwijderen Identity Provider", - "idpDelete": "Identity Provider verwijderen", - "idp": "Identiteit aanbieders", - "idpSearch": "Identiteitsaanbieders zoeken...", - "idpAdd": "Identity Provider toevoegen", - "idpClientIdRequired": "Client-ID is vereist.", - "idpClientSecretRequired": "Clientgeheim is vereist.", - "idpErrorAuthUrlInvalid": "Authenticatie-URL moet een geldige URL zijn.", - "idpErrorTokenUrlInvalid": "Token-URL moet een geldige URL zijn.", + "idpConfirmDelete": "Bevestig verwijderen identiteit provider", + "idpDelete": "Identiteit provider verwijderen", + "idp": "Identiteitsproviders", + "idpSearch": "Identiteitsproviders zoeken...", + "idpAdd": "Identiteit provider toevoegen", + "idpClientIdRequired": "Client ID is vereist.", + "idpClientSecretRequired": "Client geheim is vereist.", + "idpErrorAuthUrlInvalid": "Authenticatie URL moet een geldige URL zijn.", + "idpErrorTokenUrlInvalid": "Token URL moet een geldige URL zijn.", "idpPathRequired": "ID-pad is vereist.", "idpScopeRequired": "Toepassingsgebieden zijn vereist.", - "idpOidcDescription": "Een OpenID Connect identity provider configureren", - "idpCreatedDescription": "Identity provider succesvol aangemaakt", - "idpCreate": "Identity Provider aanmaken", - "idpCreateDescription": "Een nieuwe identiteitsprovider voor gebruikersauthenticatie configureren", - "idpSeeAll": "Zie alle identiteitsaanbieders", + "idpOidcDescription": "Een OpenID Connect identiteitsprovider configureren", + "idpCreatedDescription": "Identiteitsprovider succesvol aangemaakt", + "idpCreate": "Identiteitsprovider aanmaken", + "idpCreateDescription": "Een nieuwe identiteitsprovider voor authenticatie configureren", + "idpSeeAll": "Zie alle Identiteitsproviders", "idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider", "idpDisplayName": "Een weergavenaam voor deze identiteitsprovider", "idpAutoProvisionUsers": "Auto Provisie Gebruikers", @@ -755,10 +755,10 @@ "idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren", "idpOidcConfigure": "OAuth2/OIDC configuratie", "idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties", - "idpClientId": "Klant ID", - "idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider", - "idpClientSecret": "Clientgeheim", - "idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider", + "idpClientId": "Client ID", + "idpClientIdDescription": "De OAuth2 client ID van uw identiteitsprovider", + "idpClientSecret": "Client Secret", + "idpClientSecretDescription": "Het OAuth2 Client Secret van je identiteitsprovider", "idpAuthUrl": "URL autorisatie", "idpAuthUrlDescription": "De URL voor autorisatie OAuth2", "idpTokenUrl": "URL token", @@ -804,7 +804,7 @@ "defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.", "defaultMappingsSubmit": "Standaard toewijzingen opslaan", "orgPoliciesEdit": "Organisatie beleid bewerken", - "org": "Rekening", + "org": "Organisatie", "orgSelect": "Selecteer organisatie", "orgSearch": "Zoek in org", "orgNotFound": "Geen org gevonden.", @@ -925,7 +925,7 @@ "inviteErrorExpired": "De uitnodiging is mogelijk verlopen", "inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken", "inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink", - "pangolinSetup": "Instellen - Pangolin", + "pangolinSetup": "Setup - Pangolin", "orgNameRequired": "Organisatienaam is vereist", "orgIdRequired": "Organisatie-ID is vereist", "orgErrorCreate": "Fout opgetreden tijdens het aanmaken org", @@ -977,10 +977,10 @@ "supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!", "githubUsername": "GitHub-gebruikersnaam", "supportKeyInput": "Supporter Sleutel", - "supportKeyBuy": "Koop Supportersleutel", + "supportKeyBuy": "Koop supportersleutel", "logoutError": "Fout bij uitloggen", "signingAs": "Ingelogd als", - "serverAdmin": "Server Beheerder", + "serverAdmin": "Server beheer", "managedSelfhosted": "Beheerde Self-Hosted", "otpEnable": "Twee-factor inschakelen", "otpDisable": "Tweestapsverificatie uitschakelen", @@ -995,12 +995,12 @@ "actionGetUser": "Gebruiker ophalen", "actionGetOrgUser": "Krijg organisatie-gebruiker", "actionListOrgDomains": "Lijst organisatie domeinen", - "actionCreateSite": "Site aanmaken", + "actionCreateSite": "Site maken", "actionDeleteSite": "Site verwijderen", "actionGetSite": "Site ophalen", "actionListSites": "Sites weergeven", "actionApplyBlueprint": "Blauwdruk toepassen", - "setupToken": "Instel Token", + "setupToken": "Setup Token", "setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.", "setupTokenRequired": "Setup-token is vereist", "actionUpdateSite": "Site bijwerken", @@ -1129,7 +1129,7 @@ "sidebarOverview": "Overzicht.", "sidebarHome": "Startpagina", "sidebarSites": "Werkruimtes", - "sidebarResources": "Hulpmiddelen", + "sidebarResources": "Bronnen", "sidebarAccessControl": "Toegangs controle", "sidebarUsers": "Gebruikers", "sidebarInvitations": "Uitnodigingen", @@ -1256,50 +1256,50 @@ "domainPickerOrganizationDomains": "Organisatiedomeinen", "domainPickerProvidedDomains": "Aangeboden domeinen", "domainPickerSubdomain": "Subdomein: {subdomain}", - "domainPickerNamespace": "Naamruimte: {namespace}", + "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Meer weergeven", - "regionSelectorTitle": "Selecteer Regio", - "regionSelectorInfo": "Het selecteren van een regio helpt ons om betere prestaties te leveren voor uw locatie. U hoeft niet in dezelfde regio als uw server te zijn.", - "regionSelectorPlaceholder": "Kies een regio", - "regionSelectorComingSoon": "Komt binnenkort", - "billingLoadingSubscription": "Abonnement laden...", - "billingFreeTier": "Gratis Niveau", - "billingWarningOverLimit": "Waarschuwing: U hebt een of meer gebruikslimieten overschreden. Uw sites maken geen verbinding totdat u uw abonnement aanpast of uw gebruik aanpast.", - "billingUsageLimitsOverview": "Overzicht gebruikslimieten", - "billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@fossorial.io.", - "billingDataUsage": "Gegevensgebruik", - "billingOnlineTime": "Site Online Tijd", - "billingUsers": "Actieve Gebruikers", - "billingDomains": "Actieve Domeinen", - "billingRemoteExitNodes": "Actieve Zelfgehoste Nodes", - "billingNoLimitConfigured": "Geen limiet ingesteld", - "billingEstimatedPeriod": "Geschatte Facturatie Periode", - "billingIncludedUsage": "Opgenomen Gebruik", - "billingIncludedUsageDescription": "Gebruik inbegrepen in uw huidige abonnementsplan", - "billingFreeTierIncludedUsage": "Gratis niveau gebruikstoelagen", - "billingIncluded": "inbegrepen", - "billingEstimatedTotal": "Geschat Totaal:", - "billingNotes": "Notities", - "billingEstimateNote": "Dit is een schatting gebaseerd op uw huidige gebruik.", - "billingActualChargesMayVary": "Facturering kan variëren.", - "billingBilledAtEnd": "U wordt aan het einde van de factureringsperiode gefactureerd.", - "billingModifySubscription": "Abonnementsaanpassing", - "billingStartSubscription": "Abonnement Starten", - "billingRecurringCharge": "Terugkerende Kosten", - "billingManageSubscriptionSettings": "Beheer uw abonnementsinstellingen en voorkeuren", - "billingNoActiveSubscription": "U heeft geen actief abonnement. Start uw abonnement om gebruikslimieten te verhogen.", - "billingFailedToLoadSubscription": "Fout bij laden van abonnement", - "billingFailedToLoadUsage": "Niet gelukt om gebruik te laden", - "billingFailedToGetCheckoutUrl": "Niet gelukt om checkout URL te krijgen", - "billingPleaseTryAgainLater": "Probeer het later opnieuw.", - "billingCheckoutError": "Checkout Fout", - "billingFailedToGetPortalUrl": "Niet gelukt om portal URL te krijgen", - "billingPortalError": "Portal Fout", - "billingDataUsageInfo": "U bent in rekening gebracht voor alle gegevens die via uw beveiligde tunnels via de cloud worden verzonden. Dit omvat zowel inkomende als uitgaande verkeer over al uw sites. Wanneer u uw limiet bereikt zullen uw sites de verbinding verbreken totdat u uw abonnement upgradet of het gebruik vermindert. Gegevens worden niet in rekening gebracht bij het gebruik van knooppunten.", - "billingOnlineTimeInfo": "U wordt in rekening gebracht op basis van hoe lang uw sites verbonden blijven met de cloud. Bijvoorbeeld 44,640 minuten is gelijk aan één site met 24/7 voor een volledige maand. Wanneer u uw limiet bereikt, zal de verbinding tussen uw sites worden verbroken totdat u een upgrade van uw abonnement uitvoert of het gebruik vermindert. Tijd wordt niet belast bij het gebruik van knooppunten.", - "billingUsersInfo": "U wordt gefactureerd voor elke gebruiker in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve gebruikersaccounts in uw organisatie.", - "billingDomainInfo": "U wordt gefactureerd voor elk domein in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve domeinaccounts in uw organisatie.", - "billingRemoteExitNodesInfo": "U wordt gefactureerd voor elke beheerde Node in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve beheerde Nodes in uw organisatie.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domein niet gevonden", "domainNotFoundDescription": "Deze bron is uitgeschakeld omdat het domein niet langer in ons systeem bestaat. Stel een nieuw domein in voor deze bron.", "failed": "Mislukt", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-wijzigingen kunnen enige tijd duren om over het internet te worden verspreid. Dit kan enkele minuten tot 48 uur duren, afhankelijk van je DNS-provider en TTL-instellingen.", "resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen", "resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen", - "billingPricingCalculatorLink": "Prijs Calculator", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Ik ga akkoord met de", "termsOfService": "servicevoorwaarden", @@ -1412,41 +1412,41 @@ "addNewTarget": "Voeg nieuw doelwit toe", "targetsList": "Lijst met doelen", "targetErrorDuplicateTargetFound": "Dubbel doelwit gevonden", - "healthCheckHealthy": "Gezond", - "healthCheckUnhealthy": "Ongezond", - "healthCheckUnknown": "Onbekend", - "healthCheck": "Gezondheidscontrole", - "configureHealthCheck": "Configureer Gezondheidscontrole", - "configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in", - "enableHealthChecks": "Inschakelen Gezondheidscontroles", - "enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.", - "healthScheme": "Methode", - "healthSelectScheme": "Selecteer methode", - "healthCheckPath": "Pad", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", - "healthPort": "Poort", - "healthCheckPathDescription": "Het pad om de gezondheid status te controleren.", - "healthyIntervalSeconds": "Gezonde Interval", - "unhealthyIntervalSeconds": "Ongezonde Interval", - "IntervalSeconds": "Gezonde Interval", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Tijd is in seconden", - "retryAttempts": "Herhaal Pogingen", - "expectedResponseCodes": "Verwachte Reactiecodes", - "expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Aangepaste headers", - "customHeadersDescription": "Kopregeleinde: Header-Naam: waarde", - "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", - "saveHealthCheck": "Opslaan Gezondheidscontrole", - "healthCheckSaved": "Gezondheidscontrole Opgeslagen", - "healthCheckSavedDescription": "Gezondheidscontrole configuratie succesvol opgeslagen", - "healthCheckError": "Gezondheidscontrole Fout", - "healthCheckErrorDescription": "Er is een fout opgetreden bij het opslaan van de configuratie van de gezondheidscontrole.", - "healthCheckPathRequired": "Gezondheidscontrole pad is vereist", - "healthCheckMethodRequired": "HTTP methode is vereist", - "healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn", - "healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn", - "healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP-methode", "selectHttpMethod": "Selecteer HTTP-methode", "domainPickerSubdomainLabel": "Subdomein", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Voer een subdomein in om te zoeken en te selecteren uit beschikbare gratis domeinen.", "domainPickerFreeDomains": "Gratis Domeinen", "domainPickerSearchForAvailableDomains": "Zoek naar beschikbare domeinen", - "domainPickerNotWorkSelfHosted": "Opmerking: Gratis aangeboden domeinen zijn momenteel niet beschikbaar voor zelf-gehoste instanties.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domein", "resourceEditDomain": "Domein bewerken", "siteName": "Site Naam", @@ -1543,72 +1543,72 @@ "autoLoginError": "Auto Login Fout", "autoLoginErrorNoRedirectUrl": "Geen redirect URL ontvangen van de identity provider.", "autoLoginErrorGeneratingUrl": "Genereren van authenticatie-URL mislukt.", - "remoteExitNodeManageRemoteExitNodes": "Beheer Zelf-Gehoste", - "remoteExitNodeDescription": "Beheer knooppunten om uw netwerkverbinding uit te breiden", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Knooppunten zoeken...", - "remoteExitNodeAdd": "Voeg node toe", - "remoteExitNodeErrorDelete": "Fout bij verwijderen node", - "remoteExitNodeQuestionRemove": "Weet u zeker dat u het node {selectedNode} uit de organisatie wilt verwijderen?", - "remoteExitNodeMessageRemove": "Eenmaal verwijderd, zal het knooppunt niet langer toegankelijk zijn.", - "remoteExitNodeMessageConfirm": "Om te bevestigen, typ de naam van het knooppunt hieronder.", - "remoteExitNodeConfirmDelete": "Bevestig verwijderen node", - "remoteExitNodeDelete": "Knoop verwijderen", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Maak node", - "description": "Maak een nieuwe node aan om uw netwerkverbinding uit te breiden", - "viewAllButton": "Alle nodes weergeven", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Creatie Strategie", - "description": "Kies dit om uw node handmatig te configureren of nieuwe referenties te genereren.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adopteer Node", - "description": "Kies dit als u al de referenties voor deze node heeft" + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Genereer Sleutels", - "description": "Kies dit als u nieuwe sleutels voor het knooppunt wilt genereren" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adopteer Bestaande Node", - "description": "Voer de referenties in van het bestaande knooppunt dat u wilt adopteren", - "nodeIdLabel": "Knooppunt ID", - "nodeIdDescription": "De ID van het knooppunt dat u wilt adopteren", - "secretLabel": "Geheim", - "secretDescription": "De geheime sleutel van de bestaande node", - "submitButton": "Knooppunt adopteren" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Gegeneerde Inloggegevens", - "description": "Gebruik deze gegenereerde inloggegevens om uw node te configureren", - "nodeIdTitle": "Knooppunt ID", - "secretTitle": "Geheim", - "saveCredentialsTitle": "Voeg Inloggegevens toe aan Config", - "saveCredentialsDescription": "Voeg deze inloggegevens toe aan uw zelf-gehoste Pangolin-node configuratiebestand om de verbinding te voltooien.", - "submitButton": "Maak node" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "Node ID en Secret zijn verplicht bij het overnemen van een bestaand knooppunt" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Niet gelukt om standaarden te laden", - "defaultsNotLoaded": "Standaarden niet geladen", - "createFailed": "Fout bij het maken van node" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Node succesvol aangemaakt" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Knooppunt selectie", - "remoteExitNodeSelectionDescription": "Selecteer een node om het verkeer door te leiden voor deze lokale site", - "remoteExitNodeRequired": "Een node moet worden geselecteerd voor lokale sites", - "noRemoteExitNodesAvailable": "Geen knooppunten beschikbaar", - "noRemoteExitNodesAvailableDescription": "Er zijn geen knooppunten beschikbaar voor deze organisatie. Maak eerst een knooppunt aan om lokale sites te gebruiken.", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", "exitNode": "Exit Node", - "country": "Land", - "rulesMatchCountry": "Momenteel gebaseerd op bron IP", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Beheerde Self-Hosted", "description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internationaal Domein Gedetecteerd", "willbestoredas": "Zal worden opgeslagen als:", - "roleMappingDescription": "Bepaal hoe rollen worden toegewezen aan gebruikers wanneer ze inloggen wanneer Auto Provision is ingeschakeld.", - "selectRole": "Selecteer een rol", - "roleMappingExpression": "Expressie", - "selectRolePlaceholder": "Kies een rol", - "selectRoleDescription": "Selecteer een rol om toe te wijzen aan alle gebruikers van deze identiteitsprovider", - "roleMappingExpressionDescription": "Voer een JMESPath expressie in om rolinformatie van de ID-token te extraheren", - "idpTenantIdRequired": "Tenant ID is vereist", - "invalidValue": "Ongeldige waarde", - "idpTypeLabel": "Identiteit provider type", - "roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'", - "idpGoogleConfiguration": "Google Configuratie", - "idpGoogleConfigurationDescription": "Configureer uw Google OAuth2-referenties", - "idpGoogleClientIdDescription": "Uw Google OAuth2-client-ID", - "idpGoogleClientSecretDescription": "Uw Google OAuth2 Clientgeheim", - "idpAzureConfiguration": "Azure Entra ID configuratie", - "idpAzureConfigurationDescription": "Configureer uw Azure Entra ID OAuth2 referenties", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "jouw-tenant-id", - "idpAzureTenantIdDescription": "Uw Azure tenant ID (gevonden in Azure Active Directory overzicht)", - "idpAzureClientIdDescription": "Uw Azure App registratie Client ID", - "idpAzureClientSecretDescription": "Uw Azure App registratie Client Secret", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuratie", - "idpAzureConfigurationTitle": "Azure Entra ID configuratie", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Uw Azure App registratie Client ID", - "idpAzureClientSecretDescription2": "Uw Azure App registratie Client Secret", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "subnet": "Subnet", - "subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.", - "authPage": "Authenticatie pagina", - "authPageDescription": "De autorisatiepagina voor uw organisatie configureren", - "authPageDomain": "Authenticatie pagina domein", - "noDomainSet": "Geen domein ingesteld", - "changeDomain": "Domein wijzigen", - "selectDomain": "Domein selecteren", - "restartCertificate": "Certificaat opnieuw starten", - "editAuthPageDomain": "Authenticatiepagina domein bewerken", - "setAuthPageDomain": "Authenticatiepagina domein instellen", - "failedToFetchCertificate": "Certificaat ophalen mislukt", - "failedToRestartCertificate": "Kon certificaat niet opnieuw opstarten", - "addDomainToEnableCustomAuthPages": "Voeg een domein toe om aangepaste authenticatiepagina's voor uw organisatie in te schakelen", - "selectDomainForOrgAuthPage": "Selecteer een domein voor de authenticatiepagina van de organisatie", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Opgegeven domein", "domainPickerFreeProvidedDomain": "Gratis verstrekt domein", "domainPickerVerified": "Geverifieerd", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kon niet geldig worden gemaakt voor {domain}.", "domainPickerSubdomainSanitized": "Subdomein gesaniseerd", "domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"", - "orgAuthSignInTitle": "Meld je aan bij je organisatie", - "orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan", - "orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.", - "orgAuthSignInWithPangolin": "Log in met Pangolin", - "subscriptionRequiredToUse": "Een abonnement is vereist om deze functie te gebruiken.", - "idpDisabled": "Identiteitsaanbieders zijn uitgeschakeld.", - "orgAuthPageDisabled": "Pagina voor organisatie-authenticatie is uitgeschakeld.", - "domainRestartedDescription": "Domeinverificatie met succes opnieuw gestart", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", - "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug." + "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From e4787924e71b01f37bb97e130c2b9cac75707c2d Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:21 -0700 Subject: [PATCH 135/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 359 ++++++++++++++++++++++---------------------- 1 file changed, 182 insertions(+), 177 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index ffe77bb8..90d188b4 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Łatwiejszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.", "siteWg": "Podstawowy WireGuard", "siteWgDescription": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana jest ręczna konfiguracja NAT.", - "siteWgDescriptionSaas": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana ręczna konfiguracja NAT. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Tylko lokalne zasoby. Brak tunelu.", - "siteLocalDescriptionSaas": "Tylko zasoby lokalne. Brak tunelowania. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Zobacz wszystkie witryny", "siteTunnelDescription": "Określ jak chcesz połączyć się ze swoją stroną", "siteNewtCredentials": "Aktualne dane logowania", @@ -159,7 +159,7 @@ "resourceHTTP": "Zasób HTTPS", "resourceHTTPDescription": "Proxy do Twojej aplikacji przez HTTPS, przy użyciu poddomeny lub domeny bazowej.", "resourceRaw": "Surowy zasób TCP/UDP", - "resourceRawDescription": "Proxy do aplikacji przez TCP/UDP przy użyciu numeru portu.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Utwórz zasób", "resourceCreateDescription": "Wykonaj poniższe kroki, aby utworzyć nowy zasób", "resourceSeeAll": "Zobacz wszystkie zasoby", @@ -168,9 +168,9 @@ "siteSelect": "Wybierz witrynę", "siteSearch": "Szukaj witryny", "siteNotFound": "Nie znaleziono witryny.", - "selectCountry": "Wybierz kraj", - "searchCountries": "Szukaj krajów...", - "noCountryFound": "Nie znaleziono kraju.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Ta strona zapewni połączenie z celem.", "resourceType": "Typ zasobu", "resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu", @@ -1156,7 +1156,7 @@ "containerLabels": "Etykiety", "containerLabelsCount": "{count, plural, one {# etykieta} few {# etykiety} many {# etykiet} other {# etykiet}}", "containerLabelsTitle": "Etykiety kontenera", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Porty", "containerPortsMore": "+{count} więcej", "containerActions": "Akcje", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdomena: {subdomain}", "domainPickerNamespace": "Przestrzeń nazw: {namespace}", "domainPickerShowMore": "Pokaż więcej", - "regionSelectorTitle": "Wybierz region", - "regionSelectorInfo": "Wybór regionu pomaga nam zapewnić lepszą wydajność dla Twojej lokalizacji. Nie musisz być w tym samym regionie co Twój serwer.", - "regionSelectorPlaceholder": "Wybierz region", - "regionSelectorComingSoon": "Wkrótce dostępne", - "billingLoadingSubscription": "Ładowanie subskrypcji...", - "billingFreeTier": "Darmowy pakiet", - "billingWarningOverLimit": "Ostrzeżenie: Przekroczyłeś jeden lub więcej limitów użytkowania. Twoje witryny nie połączą się, dopóki nie zmienisz subskrypcji lub nie dostosujesz użytkowania.", - "billingUsageLimitsOverview": "Przegląd Limitów Użytkowania", - "billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@fossorial.io.", - "billingDataUsage": "Użycie danych", - "billingOnlineTime": "Czas Online Strony", - "billingUsers": "Aktywni użytkownicy", - "billingDomains": "Aktywne domeny", - "billingRemoteExitNodes": "Aktywne samodzielnie-hostowane węzły", - "billingNoLimitConfigured": "Nie skonfigurowano limitu", - "billingEstimatedPeriod": "Szacowany Okres Rozliczeniowy", - "billingIncludedUsage": "Zawarte użycie", - "billingIncludedUsageDescription": "Użycie zawarte w obecnym planie subskrypcji", - "billingFreeTierIncludedUsage": "Limity użycia dla darmowego pakietu", - "billingIncluded": "zawarte", - "billingEstimatedTotal": "Szacowana Całkowita:", - "billingNotes": "Notatki", - "billingEstimateNote": "To jest szacunkowe, oparte na Twoim obecnym użyciu.", - "billingActualChargesMayVary": "Rzeczywiste opłaty mogą się różnić.", - "billingBilledAtEnd": "Zostaniesz obciążony na koniec okresu rozliczeniowego.", - "billingModifySubscription": "Modyfikuj Subskrypcję", - "billingStartSubscription": "Rozpocznij Subskrypcję", - "billingRecurringCharge": "Opłata Cyklowa", - "billingManageSubscriptionSettings": "Zarządzaj ustawieniami i preferencjami subskrypcji", - "billingNoActiveSubscription": "Nie masz aktywnej subskrypcji. Rozpocznij subskrypcję, aby zwiększyć limity użytkowania.", - "billingFailedToLoadSubscription": "Nie udało się załadować subskrypcji", - "billingFailedToLoadUsage": "Nie udało się załadować użycia", - "billingFailedToGetCheckoutUrl": "Nie udało się uzyskać adresu URL zakupu", - "billingPleaseTryAgainLater": "Spróbuj ponownie później.", - "billingCheckoutError": "Błąd przy kasie", - "billingFailedToGetPortalUrl": "Nie udało się uzyskać adresu URL portalu", - "billingPortalError": "Błąd Portalu", - "billingDataUsageInfo": "Jesteś obciążony za wszystkie dane przesyłane przez bezpieczne tunele, gdy jesteś podłączony do chmury. Obejmuje to zarówno ruch przychodzący, jak i wychodzący we wszystkich Twoich witrynach. Gdy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie ograniczysz użycia. Dane nie będą naliczane przy użyciu węzłów.", - "billingOnlineTimeInfo": "Opłata zależy od tego, jak długo twoje strony pozostają połączone z chmurą. Na przykład 44,640 minut oznacza jedną stronę działającą 24/7 przez cały miesiąc. Kiedy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie zmniejsz jego wykorzystania. Czas nie będzie naliczany przy użyciu węzłów.", - "billingUsersInfo": "Jesteś obciążany za każdego użytkownika w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont użytkowników w twojej organizacji.", - "billingDomainInfo": "Jesteś obciążany za każdą domenę w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont domen w twojej organizacji.", - "billingRemoteExitNodesInfo": "Jesteś obciążany za każdy zarządzany węzeł w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych zarządzanych węzłów w twojej organizacji.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Nie znaleziono domeny", "domainNotFoundDescription": "Zasób jest wyłączony, ponieważ domena nie istnieje już w naszym systemie. Proszę ustawić nową domenę dla tego zasobu.", "failed": "Niepowodzenie", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Zmiany DNS mogą zająć trochę czasu na rozpropagowanie się w Internecie. Może to potrwać od kilku minut do 48 godzin, w zależności od dostawcy DNS i ustawień TTL.", "resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP", "resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP", - "billingPricingCalculatorLink": "Kalkulator Cen", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Zgadzam się z", "termsOfService": "warunkami usługi", @@ -1412,41 +1412,41 @@ "addNewTarget": "Dodaj nowy cel", "targetsList": "Lista celów", "targetErrorDuplicateTargetFound": "Znaleziono duplikat celu", - "healthCheckHealthy": "Zdrowy", - "healthCheckUnhealthy": "Niezdrowy", - "healthCheckUnknown": "Nieznany", - "healthCheck": "Kontrola Zdrowia", - "configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia", - "configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}", - "enableHealthChecks": "Włącz Kontrole Zdrowia", - "enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.", - "healthScheme": "Metoda", - "healthSelectScheme": "Wybierz metodę", - "healthCheckPath": "Ścieżka", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "Ścieżka do sprawdzania stanu zdrowia.", - "healthyIntervalSeconds": "Interwał Zdrowy", - "unhealthyIntervalSeconds": "Interwał Niezdrowy", - "IntervalSeconds": "Interwał Zdrowy", - "timeoutSeconds": "Limit Czasu", - "timeIsInSeconds": "Czas w sekundach", - "retryAttempts": "Próby Ponowienia", - "expectedResponseCodes": "Oczekiwane Kody Odpowiedzi", - "expectedResponseCodesDescription": "Kod statusu HTTP, który wskazuje zdrowy status. Jeśli pozostanie pusty, uznaje się 200-300 za zdrowy.", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Niestandardowe nagłówki", - "customHeadersDescription": "Nagłówki oddzielone: Nazwa nagłówka: wartość", - "headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.", - "saveHealthCheck": "Zapisz Kontrolę Zdrowia", - "healthCheckSaved": "Kontrola Zdrowia Zapisana", - "healthCheckSavedDescription": "Konfiguracja kontroli zdrowia została zapisana pomyślnie", - "healthCheckError": "Błąd Kontroli Zdrowia", - "healthCheckErrorDescription": "Wystąpił błąd podczas zapisywania konfiguracji kontroli zdrowia", - "healthCheckPathRequired": "Ścieżka kontroli zdrowia jest wymagana", - "healthCheckMethodRequired": "Metoda HTTP jest wymagana", - "healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund", - "healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę", - "healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "Metoda HTTP", "selectHttpMethod": "Wybierz metodę HTTP", "domainPickerSubdomainLabel": "Poddomena", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Wprowadź poddomenę, aby wyszukać i wybrać z dostępnych darmowych domen.", "domainPickerFreeDomains": "Darmowe domeny", "domainPickerSearchForAvailableDomains": "Szukaj dostępnych domen", - "domainPickerNotWorkSelfHosted": "Uwaga: Darmowe domeny nie są obecnie dostępne dla instancji samodzielnie-hostowanych.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domena", "resourceEditDomain": "Edytuj domenę", "siteName": "Nazwa strony", @@ -1543,72 +1543,72 @@ "autoLoginError": "Błąd automatycznego logowania", "autoLoginErrorNoRedirectUrl": "Nie otrzymano URL przekierowania od dostawcy tożsamości.", "autoLoginErrorGeneratingUrl": "Nie udało się wygenerować URL uwierzytelniania.", - "remoteExitNodeManageRemoteExitNodes": "Zarządzaj Samodzielnie-Hostingowane", - "remoteExitNodeDescription": "Zarządzaj węzłami w celu rozszerzenia połączenia z siecią", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Szukaj węzłów...", - "remoteExitNodeAdd": "Dodaj węzeł", - "remoteExitNodeErrorDelete": "Błąd podczas usuwania węzła", - "remoteExitNodeQuestionRemove": "Czy na pewno chcesz usunąć węzeł {selectedNode} z organizacji?", - "remoteExitNodeMessageRemove": "Po usunięciu, węzeł nie będzie już dostępny.", - "remoteExitNodeMessageConfirm": "Aby potwierdzić, wpisz nazwę węzła poniżej.", - "remoteExitNodeConfirmDelete": "Potwierdź usunięcie węzła", - "remoteExitNodeDelete": "Usuń węzeł", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Utwórz węzeł", - "description": "Utwórz nowy węzeł, aby rozszerzyć połączenie z siecią", - "viewAllButton": "Zobacz wszystkie węzły", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Strategia Tworzenia", - "description": "Wybierz to, aby ręcznie skonfigurować węzeł lub wygenerować nowe poświadczenia.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Zaadoptuj Węzeł", - "description": "Wybierz to, jeśli masz już dane logowania dla węzła." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Generuj Klucze", - "description": "Wybierz to, jeśli chcesz wygenerować nowe klucze dla węzła" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Zaadoptuj Istniejący Węzeł", - "description": "Wprowadź dane logowania istniejącego węzła, który chcesz przyjąć", - "nodeIdLabel": "ID węzła", - "nodeIdDescription": "ID istniejącego węzła, który chcesz przyjąć", - "secretLabel": "Sekret", - "secretDescription": "Sekretny klucz istniejącego węzła", - "submitButton": "Przyjmij węzeł" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Wygenerowane Poświadczenia", - "description": "Użyj tych danych logowania, aby skonfigurować węzeł", - "nodeIdTitle": "ID węzła", - "secretTitle": "Sekret", - "saveCredentialsTitle": "Dodaj Poświadczenia do Konfiguracji", - "saveCredentialsDescription": "Dodaj te poświadczenia do pliku konfiguracyjnego swojego samodzielnie-hostowanego węzła Pangolin, aby zakończyć połączenie.", - "submitButton": "Utwórz węzeł" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "Identyfikator węzła i sekret są wymagane podczas przyjmowania istniejącego węzła" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Nie udało się załadować domyślnych ustawień", - "defaultsNotLoaded": "Domyślne ustawienia nie zostały załadowane", - "createFailed": "Nie udało się utworzyć węzła" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Węzeł utworzony pomyślnie" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Wybór węzła", - "remoteExitNodeSelectionDescription": "Wybierz węzeł do przekierowania ruchu dla tej lokalnej witryny", - "remoteExitNodeRequired": "Węzeł musi być wybrany dla lokalnych witryn", - "noRemoteExitNodesAvailable": "Brak dostępnych węzłów", - "noRemoteExitNodesAvailableDescription": "Węzły nie są dostępne dla tej organizacji. Utwórz węzeł, aby używać lokalnych witryn.", - "exitNode": "Węzeł Wyjściowy", - "country": "Kraj", - "rulesMatchCountry": "Obecnie bazuje na adresie IP źródła", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Zarządzane Samodzielnie-Hostingowane", "description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Wykryto międzynarodową domenę", "willbestoredas": "Będą przechowywane jako:", - "roleMappingDescription": "Określ jak role są przypisywane do użytkowników podczas logowania się, gdy automatyczne świadczenie jest włączone.", - "selectRole": "Wybierz rolę", - "roleMappingExpression": "Wyrażenie", - "selectRolePlaceholder": "Wybierz rolę", - "selectRoleDescription": "Wybierz rolę do przypisania wszystkim użytkownikom od tego dostawcy tożsamości", - "roleMappingExpressionDescription": "Wprowadź wyrażenie JMESŚcieżki, aby wyodrębnić informacje o roli z tokenu ID", - "idpTenantIdRequired": "ID lokatora jest wymagane", - "invalidValue": "Nieprawidłowa wartość", - "idpTypeLabel": "Typ dostawcy tożsamości", - "roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'", - "idpGoogleConfiguration": "Konfiguracja Google", - "idpGoogleConfigurationDescription": "Skonfiguruj swoje poświadczenia Google OAuth2", - "idpGoogleClientIdDescription": "Twój identyfikator klienta Google OAuth2", - "idpGoogleClientSecretDescription": "Twój klucz klienta Google OAuth2", - "idpAzureConfiguration": "Konfiguracja Azure Entra ID", - "idpAzureConfigurationDescription": "Skonfiguruj swoje dane logowania OAuth2 Azure Entra", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "twoj-lokator", - "idpAzureTenantIdDescription": "Twój identyfikator dzierżawcy Azure (znaleziony w Podglądzie Azure Active Directory", - "idpAzureClientIdDescription": "Twój identyfikator klienta rejestracji aplikacji Azure", - "idpAzureClientSecretDescription": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Konfiguracja Google", - "idpAzureConfigurationTitle": "Konfiguracja Azure Entra ID", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Twój identyfikator klienta rejestracji aplikacji Azure", - "idpAzureClientSecretDescription2": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Dostawca Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Podsieć", - "subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.", - "authPage": "Strona uwierzytelniania", - "authPageDescription": "Skonfiguruj stronę uwierzytelniania dla swojej organizacji", - "authPageDomain": "Domena strony uwierzytelniania", - "noDomainSet": "Nie ustawiono domeny", - "changeDomain": "Zmień domenę", - "selectDomain": "Wybierz domenę", - "restartCertificate": "Uruchom ponownie certyfikat", - "editAuthPageDomain": "Edytuj domenę strony uwierzytelniania", - "setAuthPageDomain": "Ustaw domenę strony uwierzytelniania", - "failedToFetchCertificate": "Nie udało się pobrać certyfikatu", - "failedToRestartCertificate": "Nie udało się ponownie uruchomić certyfikatu", - "addDomainToEnableCustomAuthPages": "Dodaj domenę, aby włączyć niestandardowe strony uwierzytelniania dla Twojej organizacji", - "selectDomainForOrgAuthPage": "Wybierz domenę dla strony uwierzytelniania organizacji", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Dostarczona domena", "domainPickerFreeProvidedDomain": "Darmowa oferowana domena", "domainPickerVerified": "Zweryfikowano", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nie może być poprawne dla {domain}.", "domainPickerSubdomainSanitized": "Poddomena oczyszczona", "domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"", - "orgAuthSignInTitle": "Zaloguj się do swojej organizacji", - "orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować", - "orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.", - "orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin", - "subscriptionRequiredToUse": "Do korzystania z tej funkcji wymagana jest subskrypcja.", - "idpDisabled": "Dostawcy tożsamości są wyłączeni", - "orgAuthPageDisabled": "Strona autoryzacji organizacji jest wyłączona.", - "domainRestartedDescription": "Weryfikacja domeny zrestartowana pomyślnie", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", - "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj." + "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From ab7ac9cb6086e19ad338d00d66332d4f3ef5be8d Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:22 -0700 Subject: [PATCH 136/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 631 ++++++++++++++++++++++---------------------- 1 file changed, 318 insertions(+), 313 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 151ee73f..3c5ae253 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -8,25 +8,25 @@ "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", - "componentsErrorNoMemberCreate": "Não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", - "componentsErrorNoMember": "Não é atualmente um membro de nenhuma organização.", + "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", "welcome": "Bem-vindo ao Pangolin", "welcomeTo": "Bem-vindo ao", "componentsCreateOrg": "Criar uma organização", - "componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", + "componentsMember": "Você é membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", - "dismiss": "Rejeitar", + "dismiss": "Descartar", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", - "inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.", - "inviteErrorUser": "Lamentamos, mas parece que o convite que está a tentar aceder não é para este utilizador.", - "inviteLoginUser": "Verifique se você está logado como o utilizador correto.", - "inviteErrorNoUser": "Desculpe, mas parece que o convite que está a tentar aceder não é para um utilizador que existe.", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", + "inviteLoginUser": "Verifique se você está logado como o usuário correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", "inviteCreateUser": "Por favor, crie uma conta primeiro.", - "goHome": "Voltar ao inicio", - "inviteLogInOtherUser": "Fazer login como um utilizador diferente", + "goHome": "Ir para casa", + "inviteLogInOtherUser": "Fazer login como um usuário diferente", "createAnAccount": "Crie uma conta", - "inviteNotAccepted": "Convite não aceite", + "inviteNotAccepted": "Convite não aceito", "authCreateAccount": "Crie uma conta para começar", "authNoAccount": "Não possui uma conta?", "email": "e-mail", @@ -34,23 +34,23 @@ "confirmPassword": "Confirmar senha", "createAccount": "Criar conta", "viewSettings": "Visualizar configurações", - "delete": "apagar", + "delete": "excluir", "name": "Nome:", "online": "Disponível", "offline": "Desconectado", "site": "site", - "dataIn": "Dados de entrada", + "dataIn": "Dados em", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", - "siteConfirmDelete": "Confirmar que pretende apagar o site", + "siteConfirmDelete": "Confirmar exclusão do site", "siteDelete": "Excluir site", "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", - "siteManageSites": "Gerir sites", + "siteManageSites": "Gerenciar sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", @@ -79,10 +79,10 @@ "operatingSystem": "Sistema operacional", "commands": "Comandos", "recommended": "Recomendados", - "siteNewtDescription": "Para a melhor experiência do utilizador, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", - "siteErrorDelete": "Erro ao apagar site", + "siteErrorDelete": "Erro ao excluir site", "siteErrorUpdate": "Falha ao atualizar site", "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", "siteUpdated": "Site atualizado", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "A maneira mais fácil de criar um ponto de entrada na sua rede. Nenhuma configuração extra.", "siteWg": "WireGuard Básico", "siteWgDescription": "Use qualquer cliente do WireGuard para estabelecer um túnel. Configuração manual NAT é necessária.", - "siteWgDescriptionSaas": "Use qualquer cliente WireGuard para estabelecer um túnel. Configuração manual NAT necessária. SOMENTE FUNCIONA EM NODES AUTO-HOSPEDADOS", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Recursos locais apenas. Sem túneis.", - "siteLocalDescriptionSaas": "Apenas recursos locais. Sem tunelamento. SOMENTE FUNCIONA EM NODES AUTO-HOSPEDADOS", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Ver todos os sites", "siteTunnelDescription": "Determine como você deseja se conectar ao seu site", "siteNewtCredentials": "Credenciais Novas", @@ -105,12 +105,12 @@ "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", "siteInfo": "Informações do Site", "status": "SItuação", - "shareTitle": "Gerir links partilhados", + "shareTitle": "Gerenciar links de compartilhamento", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", "shareCreate": "Criar Link de Compartilhamento", - "shareErrorDelete": "Falha ao apagar o link", - "shareErrorDeleteMessage": "Ocorreu um erro ao apagar o link", + "shareErrorDelete": "Falha ao excluir o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", "shareDeleted": "Link excluído", "shareDeletedDescription": "O link foi eliminado", "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", @@ -127,13 +127,13 @@ "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", "shareErrorCreate": "Falha ao criar link de compartilhamento", "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", - "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", + "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", "shareTitleOptional": "Título (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", - "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", - "shareAccessHint": "Qualquer um com este link pode aceder o recurso. Compartilhe com cuidado.", + "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", "resourcesNotFound": "Nenhum recurso encontrado", @@ -145,11 +145,11 @@ "expires": "Expira", "never": "nunca", "shareErrorSelectResource": "Por favor, selecione um recurso", - "resourceTitle": "Gerir Recursos", + "resourceTitle": "Gerenciar Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", - "resourceErrorDelte": "Erro ao apagar recurso", + "resourceErrorDelte": "Erro ao excluir recurso", "authentication": "Autenticação", "protected": "Protegido", "notProtected": "Não Protegido", @@ -159,7 +159,7 @@ "resourceHTTP": "Recurso HTTPS", "resourceHTTPDescription": "O proxy solicita ao seu aplicativo via HTTPS usando um subdomínio ou domínio base.", "resourceRaw": "Recurso TCP/UDP bruto", - "resourceRawDescription": "O proxy solicita ao seu aplicativo sobre TCP/UDP usando um número de porta.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Criar Recurso", "resourceCreateDescription": "Siga os passos abaixo para criar um novo recurso", "resourceSeeAll": "Ver todos os recursos", @@ -168,12 +168,12 @@ "siteSelect": "Selecionar site", "siteSearch": "Procurar no site", "siteNotFound": "Nenhum site encontrado.", - "selectCountry": "Selecionar país", - "searchCountries": "Buscar países...", - "noCountryFound": "Nenhum país encontrado.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Este site fornecerá conectividade ao destino.", "resourceType": "Tipo de Recurso", - "resourceTypeDescription": "Determine como você deseja aceder seu recurso", + "resourceTypeDescription": "Determine como você deseja acessar seu recurso", "resourceHTTPSSettings": "Configurações de HTTPS", "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", "domainType": "Tipo de domínio", @@ -195,7 +195,7 @@ "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", "resourceDelete": "Excluir Recurso", - "resourceDeleteConfirm": "Confirmar que pretende apagar o recurso", + "resourceDeleteConfirm": "Confirmar exclusão de recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -211,14 +211,14 @@ "passToAuth": "Passar para Autenticação", "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", - "orgGeneralSettingsDescription": "Gerir os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Guardar configurações gerais", - "saveSettings": "Guardar Configurações", + "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", + "saveGeneralSettings": "Salvar configurações gerais", + "saveSettings": "Salvar Configurações", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", - "orgDeleteConfirm": "Confirmar que pretende apagar a organização", - "orgMessageRemove": "Esta ação é irreversível e apagará todos os dados associados.", + "orgDeleteConfirm": "Confirmar exclusão da organização", + "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", "orgUpdated": "Organização atualizada", @@ -227,29 +227,29 @@ "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", "orgErrorFetch": "Falha ao buscar organizações", "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", - "orgErrorDelete": "Falha ao apagar organização", - "orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.", + "orgErrorDelete": "Falha ao excluir organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgMissing": "ID da Organização Ausente", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", - "accessUsersManage": "Gerir Utilizadores", - "accessUsersDescription": "Convidar utilizadores e adicioná-los a funções para gerir o acesso à sua organização", - "accessUsersSearch": "Procurar utilizadores...", + "accessUsersManage": "Gerenciar Usuários", + "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", + "accessUsersSearch": "Procurar usuários...", "accessUserCreate": "Criar Usuário", - "accessUserRemove": "Remover utilizador", + "accessUserRemove": "Remover usuário", "username": "Usuário:", "identityProvider": "Provedor de Identidade", "role": "Funções", "nameRequired": "O nome é obrigatório", - "accessRolesManage": "Gerir Funções", - "accessRolesDescription": "Configurar funções para gerir o acesso à sua organização", + "accessRolesManage": "Gerenciar Funções", + "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", "accessRolesSearch": "Pesquisar funções...", "accessRolesAdd": "Adicionar função", "accessRoleDelete": "Excluir Papel", "description": "Descrição:", "inviteTitle": "Convites Abertos", - "inviteDescription": "Gerir seus convites para outros utilizadores", + "inviteDescription": "Gerencie seus convites para outros usuários", "inviteSearch": "Procurar convites...", "minutes": "minutos", "hours": "horas", @@ -267,7 +267,7 @@ "apiKeysGeneralSettings": "Permissões", "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", "apiKeysList": "Sua Chave API", - "apiKeysSave": "Guardar Sua Chave API", + "apiKeysSave": "Salvar Sua Chave API", "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", "apiKeysInfo": "Sua chave API é:", "apiKeysConfirmCopy": "Eu copiei a chave API", @@ -280,33 +280,33 @@ "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", "apiKeysPermissionsGeneralSettings": "Permissões", "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", - "apiKeysPermissionsSave": "Guardar Permissões", + "apiKeysPermissionsSave": "Salvar Permissões", "apiKeysPermissionsTitle": "Permissões", "apiKeys": "Chaves API", "searchApiKeys": "Pesquisar chaves API...", "apiKeysAdd": "Gerar Chave API", - "apiKeysErrorDelete": "Erro ao apagar chave API", - "apiKeysErrorDeleteMessage": "Erro ao apagar chave API", + "apiKeysErrorDelete": "Erro ao excluir chave API", + "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", - "apiKeysManage": "Gerir Chaves API", + "apiKeysManage": "Gerenciar Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", "apiKeysSettings": "Configurações de {apiKeyName}", - "userTitle": "Gerir Todos os Utilizadores", - "userDescription": "Visualizar e gerir todos os utilizadores no sistema", + "userTitle": "Gerenciar Todos os Usuários", + "userDescription": "Visualizar e gerenciar todos os usuários no sistema", "userAbount": "Sobre a Gestão de Usuário", - "userAbountDescription": "Esta tabela exibe todos os objetos root do utilizador. Cada utilizador pode pertencer a várias organizações. Remover um utilizador de uma organização não exclui seu objeto de utilizador raiz - ele permanecerá no sistema. Para remover completamente um utilizador do sistema, você deve apagar seu objeto raiz usando a ação de apagar nesta tabela.", - "userServer": "Utilizadores do Servidor", - "userSearch": "Pesquisar utilizadores do servidor...", - "userErrorDelete": "Erro ao apagar utilizador", + "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", + "userServer": "Usuários do Servidor", + "userSearch": "Pesquisar usuários do servidor...", + "userErrorDelete": "Erro ao excluir usuário", "userDeleteConfirm": "Confirmar Exclusão do Usuário", - "userDeleteServer": "Excluir utilizador do servidor", - "userMessageRemove": "O utilizador será removido de todas as organizações e será completamente removido do servidor.", - "userMessageConfirm": "Para confirmar, por favor digite o nome do utilizador abaixo.", - "userQuestionRemove": "Tem certeza que deseja apagar o {selectedUser} permanentemente do servidor?", + "userDeleteServer": "Excluir usuário do servidor", + "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", + "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", "valid": "Válido", "numberOfSites": "Número de sites", @@ -317,8 +317,8 @@ "licenseTermsAgree": "Você deve concordar com os termos da licença", "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", - "licenseErrorKeyDelete": "Falha ao apagar chave de licença", - "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao apagar a chave de licença.", + "licenseErrorKeyDelete": "Falha ao excluir chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", "licenseKeyDeleted": "Chave da licença excluída", "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", @@ -339,13 +339,13 @@ "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", - "licenseQuestionRemove": "Tem certeza que deseja apagar a chave de licença {selectedKey}?", + "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", "licenseKeyDelete": "Excluir Chave de Licença", - "licenseKeyDeleteConfirm": "Confirmar que pretende apagar a chave de licença", - "licenseTitle": "Gerir Status da Licença", - "licenseTitleDescription": "Visualizar e gerir chaves de licença no sistema", + "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", + "licenseTitle": "Gerenciar Status da Licença", + "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", "licenseHost": "Licença do host", - "licenseHostDescription": "Gerir a chave de licença principal do host.", + "licenseHostDescription": "Gerenciar a chave de licença principal do host.", "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", @@ -373,37 +373,37 @@ "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", - "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o utilizador novamente mais tarde.", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", "inviteRemoveConfirm": "Confirmar Remoção do Convite", "inviteRegenerated": "Convite Regenerado", "inviteSent": "Um novo convite foi enviado para {email}.", - "inviteSentEmail": "Enviar notificação por e-mail ao utilizador", + "inviteSentEmail": "Enviar notificação por e-mail ao usuário", "inviteGenerate": "Um novo convite foi gerado para {email}.", "inviteDuplicateError": "Convite Duplicado", - "inviteDuplicateErrorDescription": "Já existe um convite para este utilizador.", + "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", "inviteRateLimitError": "Limite de Taxa Excedido", - "inviteRateLimitErrorDescription": "Excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", "inviteRegenerateError": "Falha ao Regenerar Convite", "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", "inviteValidityPeriod": "Período de Validade", "inviteValidityPeriodSelect": "Selecione o período de validade", - "inviteRegenerateMessage": "O convite foi regenerado. O utilizador deve aceder o link abaixo para aceitar o convite.", + "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", "inviteRegenerateButton": "Regenerar", "expiresAt": "Expira em", "accessRoleUnknown": "Função Desconhecida", "placeholder": "Espaço reservado", - "userErrorOrgRemove": "Falha ao remover utilizador", - "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o utilizador.", + "userErrorOrgRemove": "Falha ao remover usuário", + "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", "userOrgRemoved": "Usuário removido", - "userOrgRemovedDescription": "O utilizador {email} foi removido da organização.", + "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", - "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", - "userMessageOrgConfirm": "Para confirmar, digite o nome do utilizador abaixo.", + "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", + "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrg": "Remover Usuário da Organização", - "users": "Utilizadores", + "users": "Usuários", "accessRoleMember": "Membro", "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", @@ -411,7 +411,7 @@ "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", - "usernameRequired": "Nome de utilizador é obrigatório", + "usernameRequired": "Nome de usuário é obrigatório", "idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "accessRoleErrorFetch": "Falha ao buscar funções", @@ -419,51 +419,51 @@ "idpErrorFetch": "Falha ao buscar provedores de identidade", "idpErrorFetchDescription": "Ocorreu um erro ao buscar provedores de identidade", "userErrorExists": "Usuário já existe", - "userErrorExistsDescription": "Este utilizador já é membro da organização.", - "inviteError": "Falha ao convidar utilizador", - "inviteErrorDescription": "Ocorreu um erro ao convidar o utilizador", + "userErrorExistsDescription": "Este usuário já é membro da organização.", + "inviteError": "Falha ao convidar usuário", + "inviteErrorDescription": "Ocorreu um erro ao convidar o usuário", "userInvited": "Usuário convidado", - "userInvitedDescription": "O utilizador foi convidado com sucesso.", - "userErrorCreate": "Falha ao criar utilizador", - "userErrorCreateDescription": "Ocorreu um erro ao criar o utilizador", + "userInvitedDescription": "O usuário foi convidado com sucesso.", + "userErrorCreate": "Falha ao criar usuário", + "userErrorCreateDescription": "Ocorreu um erro ao criar o usuário", "userCreated": "Usuário criado", - "userCreatedDescription": "O utilizador foi criado com sucesso.", + "userCreatedDescription": "O usuário foi criado com sucesso.", "userTypeInternal": "Usuário Interno", - "userTypeInternalDescription": "Convidar um utilizador para se juntar à sua organização diretamente.", + "userTypeInternalDescription": "Convidar um usuário para se juntar à sua organização diretamente.", "userTypeExternal": "Usuário Externo", - "userTypeExternalDescription": "Criar um utilizador com um provedor de identidade externo.", - "accessUserCreateDescription": "Siga os passos abaixo para criar um novo utilizador", - "userSeeAll": "Ver Todos os Utilizadores", + "userTypeExternalDescription": "Criar um usuário com um provedor de identidade externo.", + "accessUserCreateDescription": "Siga os passos abaixo para criar um novo usuário", + "userSeeAll": "Ver Todos os Usuários", "userTypeTitle": "Tipo de Usuário", - "userTypeDescription": "Determine como você deseja criar o utilizador", + "userTypeDescription": "Determine como você deseja criar o usuário", "userSettings": "Informações do Usuário", - "userSettingsDescription": "Insira os detalhes para o novo utilizador", - "inviteEmailSent": "Enviar e-mail de convite para o utilizador", + "userSettingsDescription": "Insira os detalhes para o novo usuário", + "inviteEmailSent": "Enviar e-mail de convite para o usuário", "inviteValid": "Válido Por", "selectDuration": "Selecionar duração", "accessRoleSelect": "Selecionar função", - "inviteEmailSentDescription": "Um e-mail foi enviado ao utilizador com o link de acesso abaixo. Eles devem aceder ao link para aceitar o convite.", - "inviteSentDescription": "O utilizador foi convidado. Eles devem aceder ao link abaixo para aceitar o convite.", + "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", + "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, one {# dia} other {# dias}}.", "idpTitle": "Informações Gerais", - "idpSelect": "Selecione o provedor de identidade para o utilizador externo", - "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar utilizadores externos.", - "usernameUniq": "Isto deve corresponder ao nome de utilizador único que existe no provedor de identidade selecionado.", + "idpSelect": "Selecione o provedor de identidade para o usuário externo", + "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", + "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", "emailOptional": "E-mail (Opcional)", "nameOptional": "Nome (Opcional)", - "accessControls": "Controlos de Acesso", - "userDescription2": "Gerir as configurações deste utilizador", - "accessRoleErrorAdd": "Falha ao adicionar utilizador à função", - "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar utilizador à função.", + "accessControls": "Controles de Acesso", + "userDescription2": "Gerenciar as configurações deste usuário", + "accessRoleErrorAdd": "Falha ao adicionar usuário à função", + "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar usuário à função.", "userSaved": "Usuário salvo", - "userSavedDescription": "O utilizador foi atualizado.", + "userSavedDescription": "O usuário foi atualizado.", "autoProvisioned": "Auto provisionado", - "autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade", - "accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização", - "accessControlsSubmit": "Guardar Controlos de Acesso", + "autoProvisionedDescription": "Permitir que este usuário seja gerenciado automaticamente pelo provedor de identidade", + "accessControlsDescription": "Gerencie o que este usuário pode acessar e fazer na organização", + "accessControlsSubmit": "Salvar Controles de Acesso", "roles": "Funções", - "accessUsersRoles": "Gerir Utilizadores e Funções", - "accessUsersRolesDescription": "Convide utilizadores e adicione-os a funções para gerir o acesso à sua organização", + "accessUsersRoles": "Gerenciar Usuários e Funções", + "accessUsersRolesDescription": "Convide usuários e adicione-os a funções para gerenciar o acesso à sua organização", "key": "Chave", "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", @@ -497,7 +497,7 @@ "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", - "targetTlsSubmit": "Guardar Configurações", + "targetTlsSubmit": "Salvar Configurações", "targets": "Configuração de Alvos", "targetsDescription": "Configure alvos para rotear tráfego para seus serviços de backend", "targetStickySessions": "Ativar Sessões Persistentes", @@ -506,12 +506,12 @@ "targetSubmit": "Adicionar Alvo", "targetNoOne": "Sem alvos. Adicione um alvo usando o formulário.", "targetNoOneDescription": "Adicionar mais de um alvo acima habilitará o balanceamento de carga.", - "targetsSubmit": "Guardar Alvos", + "targetsSubmit": "Salvar Alvos", "proxyAdditional": "Configurações Adicionais de Proxy", "proxyAdditionalDescription": "Configure como seu recurso lida com configurações de proxy", "proxyCustomHeader": "Cabeçalho Host Personalizado", "proxyCustomHeaderDescription": "O cabeçalho host para definir ao fazer proxy de requisições. Deixe vazio para usar o padrão.", - "proxyAdditionalSubmit": "Guardar Configurações de Proxy", + "proxyAdditionalSubmit": "Salvar Configurações de Proxy", "subnetMaskErrorInvalid": "Máscara de subnet inválida. Deve estar entre 0 e 32.", "ipAddressErrorInvalidFormat": "Formato de endereço IP inválido", "ipAddressErrorInvalidOctet": "Octeto de endereço IP inválido", @@ -564,7 +564,7 @@ "ruleSubmit": "Adicionar Regra", "rulesNoOne": "Sem regras. Adicione uma regra usando o formulário.", "rulesOrder": "As regras são avaliadas por prioridade em ordem ascendente.", - "rulesSubmit": "Guardar Regras", + "rulesSubmit": "Salvar Regras", "resourceErrorCreate": "Erro ao criar recurso", "resourceErrorCreateDescription": "Ocorreu um erro ao criar o recurso", "resourceErrorCreateMessage": "Erro ao criar recurso:", @@ -579,7 +579,7 @@ "resourcesDescription": "Recursos são proxies para aplicações executando em sua rede privada. Crie um recurso para qualquer serviço HTTP/HTTPS ou TCP/UDP bruto em sua rede privada. Cada recurso deve estar conectado a um site para habilitar conectividade privada e segura através de um túnel WireGuard criptografado.", "resourcesWireGuardConnect": "Conectividade segura com criptografia WireGuard", "resourcesMultipleAuthenticationMethods": "Configure múltiplos métodos de autenticação", - "resourcesUsersRolesAccess": "Controle de acesso baseado em utilizadores e funções", + "resourcesUsersRolesAccess": "Controle de acesso baseado em usuários e funções", "resourcesErrorUpdate": "Falha ao alternar recurso", "resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso", "access": "Acesso", @@ -609,7 +609,7 @@ "pangolinSettings": "Configurações - Pangolin", "accessRoleYour": "Sua função:", "accessRoleSelect2": "Selecionar uma função", - "accessUserSelect": "Selecionar um utilizador", + "accessUserSelect": "Selecionar um usuário", "otpEmailEnter": "Digite um e-mail", "otpEmailEnterDescription": "Pressione enter para adicionar um e-mail após digitá-lo no campo de entrada.", "otpEmailErrorInvalid": "Endereço de e-mail inválido. O caractere curinga (*) deve ser a parte local inteira.", @@ -619,8 +619,8 @@ "otpEmailTitleDescription": "Requer autenticação baseada em e-mail para acesso ao recurso", "otpEmailWhitelist": "Lista de E-mails Permitidos", "otpEmailWhitelistList": "E-mails na Lista Permitida", - "otpEmailWhitelistListDescription": "Apenas utilizadores com estes endereços de e-mail poderão aceder este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", - "otpEmailWhitelistSave": "Guardar Lista Permitida", + "otpEmailWhitelistListDescription": "Apenas usuários com estes endereços de e-mail poderão acessar este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", + "otpEmailWhitelistSave": "Salvar Lista Permitida", "passwordAdd": "Adicionar Senha", "passwordRemove": "Remover Senha", "pincodeAdd": "Adicionar Código PIN", @@ -660,14 +660,14 @@ "resourcePincodeSetupDescription": "O código PIN do recurso foi definido com sucesso", "resourcePincodeSetupTitle": "Definir Código PIN", "resourcePincodeSetupTitleDescription": "Defina um código PIN para proteger este recurso", - "resourceRoleDescription": "Administradores sempre podem aceder este recurso.", - "resourceUsersRoles": "Utilizadores e Funções", - "resourceUsersRolesDescription": "Configure quais utilizadores e funções podem visitar este recurso", - "resourceUsersRolesSubmit": "Guardar Utilizadores e Funções", + "resourceRoleDescription": "Administradores sempre podem acessar este recurso.", + "resourceUsersRoles": "Usuários e Funções", + "resourceUsersRolesDescription": "Configure quais usuários e funções podem visitar este recurso", + "resourceUsersRolesSubmit": "Salvar Usuários e Funções", "resourceWhitelistSave": "Salvo com sucesso", "resourceWhitelistSaveDescription": "As configurações da lista permitida foram salvas", "ssoUse": "Usar SSO da Plataforma", - "ssoUseDescription": "Os utilizadores existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", + "ssoUseDescription": "Os usuários existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", "proxyErrorInvalidPort": "Número da porta inválido", "subdomainErrorInvalid": "Subdomínio inválido", "domainErrorFetch": "Erro ao buscar domínios", @@ -693,7 +693,7 @@ "siteDestination": "Site de Destino", "searchSites": "Pesquisar sites", "accessRoleCreate": "Criar Função", - "accessRoleCreateDescription": "Crie uma nova função para agrupar utilizadores e gerir suas permissões.", + "accessRoleCreateDescription": "Crie uma nova função para agrupar usuários e gerenciar suas permissões.", "accessRoleCreateSubmit": "Criar Função", "accessRoleCreated": "Função criada", "accessRoleCreatedDescription": "A função foi criada com sucesso.", @@ -703,13 +703,13 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a apagar a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", "accessRoleRemoved": "Função removida", "accessRoleRemovedDescription": "A função foi removida com sucesso.", - "accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.", + "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", @@ -919,8 +919,8 @@ "idpErrorNotFound": "IdP não encontrado", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", - "inviteErrorWrongUser": "O convite não é para este utilizador", - "inviteErrorUserNotExists": "O utilizador não existe. Por favor, crie uma conta primeiro.", + "inviteErrorWrongUser": "O convite não é para este usuário", + "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", "inviteErrorExpired": "O convite pode ter expirado", "inviteErrorRevoked": "O convite pode ter sido revogado", @@ -935,7 +935,7 @@ "home": "Início", "accessControl": "Controle de Acesso", "settings": "Configurações", - "usersAll": "Todos os Utilizadores", + "usersAll": "Todos os Usuários", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", "noResults": "Nenhum resultado encontrado.", @@ -988,8 +988,8 @@ "licenseTierProfessionalRequired": "Edição Profissional Necessária", "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", "actionGetOrg": "Obter Organização", - "updateOrgUser": "Atualizar utilizador Org", - "createOrgUser": "Criar utilizador Org", + "updateOrgUser": "Atualizar usuário Org", + "createOrgUser": "Criar usuário Org", "actionUpdateOrg": "Atualizar Organização", "actionUpdateUser": "Atualizar Usuário", "actionGetUser": "Obter Usuário", @@ -1136,8 +1136,8 @@ "sidebarRoles": "Papéis", "sidebarShareableLinks": "Links compartilháveis", "sidebarApiKeys": "Chaves API", - "sidebarSettings": "Configurações", - "sidebarAllUsers": "Todos os utilizadores", + "sidebarSettings": "Confirgurações", + "sidebarAllUsers": "Todos os usuários", "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "sidebarClients": "Clientes (Beta)", @@ -1190,7 +1190,7 @@ "loading": "Carregando", "restart": "Reiniciar", "domains": "Domínios", - "domainsDescription": "Gerir domínios para sua organização", + "domainsDescription": "Gerencie domínios para sua organização", "domainsSearch": "Pesquisar domínios...", "domainAdd": "Adicionar Domínio", "domainAddDescription": "Registre um novo domínio com sua organização", @@ -1218,7 +1218,7 @@ "pending": "Pendente", "sidebarBilling": "Faturamento", "billing": "Faturamento", - "orgBillingDescription": "Gerir suas informações de faturação e assinaturas", + "orgBillingDescription": "Gerencie suas informações de faturamento e assinaturas", "github": "GitHub", "pangolinHosted": "Hospedagem Pangolin", "fossorial": "Fossorial", @@ -1233,7 +1233,7 @@ "completeSetup": "Configuração Completa", "accountSetupSuccess": "Configuração da conta concluída! Bem-vindo ao Pangolin!", "documentation": "Documentação", - "saveAllSettings": "Guardar Todas as Configurações", + "saveAllSettings": "Salvar Todas as Configurações", "settingsUpdated": "Configurações atualizadas", "settingsUpdatedDescription": "Todas as configurações foram atualizadas com sucesso", "settingsErrorUpdate": "Falha ao atualizar configurações", @@ -1258,55 +1258,55 @@ "domainPickerSubdomain": "Subdomínio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostrar Mais", - "regionSelectorTitle": "Selecionar Região", - "regionSelectorInfo": "Selecionar uma região nos ajuda a fornecer melhor desempenho para sua localização. Você não precisa estar na mesma região que seu servidor.", - "regionSelectorPlaceholder": "Escolher uma região", - "regionSelectorComingSoon": "Em breve", - "billingLoadingSubscription": "Carregando assinatura...", - "billingFreeTier": "Plano Gratuito", - "billingWarningOverLimit": "Aviso: Você ultrapassou um ou mais limites de uso. Seus sites não se conectarão até você modificar sua assinatura ou ajustar seu uso.", - "billingUsageLimitsOverview": "Visão Geral dos Limites de Uso", - "billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@fossorial.io.", - "billingDataUsage": "Uso de Dados", - "billingOnlineTime": "Tempo Online do Site", - "billingUsers": "Usuários Ativos", - "billingDomains": "Domínios Ativos", - "billingRemoteExitNodes": "Nodos Auto-Hospedados Ativos", - "billingNoLimitConfigured": "Nenhum limite configurado", - "billingEstimatedPeriod": "Período Estimado de Cobrança", - "billingIncludedUsage": "Uso Incluído", - "billingIncludedUsageDescription": "Uso incluído no seu plano de assinatura atual", - "billingFreeTierIncludedUsage": "Limites de uso do plano gratuito", - "billingIncluded": "incluído", - "billingEstimatedTotal": "Total Estimado:", - "billingNotes": "Notas", - "billingEstimateNote": "Esta é uma estimativa baseada no seu uso atual.", - "billingActualChargesMayVary": "As cobranças reais podem variar.", - "billingBilledAtEnd": "Sua cobrança será feita ao final do período de cobrança.", - "billingModifySubscription": "Modificar Assinatura", - "billingStartSubscription": "Iniciar Assinatura", - "billingRecurringCharge": "Cobrança Recorrente", - "billingManageSubscriptionSettings": "Gerenciar as configurações e preferências da sua assinatura", - "billingNoActiveSubscription": "Você não tem uma assinatura ativa. Inicie sua assinatura para aumentar os limites de uso.", - "billingFailedToLoadSubscription": "Falha ao carregar assinatura", - "billingFailedToLoadUsage": "Falha ao carregar uso", - "billingFailedToGetCheckoutUrl": "Falha ao obter URL de checkout", - "billingPleaseTryAgainLater": "Por favor, tente novamente mais tarde.", - "billingCheckoutError": "Erro de Checkout", - "billingFailedToGetPortalUrl": "Falha ao obter URL do portal", - "billingPortalError": "Erro do Portal", - "billingDataUsageInfo": "Você é cobrado por todos os dados transferidos através de seus túneis seguros quando conectado à nuvem. Isso inclui o tráfego de entrada e saída em todos os seus sites. Quando você atingir o seu limite, seus sites desconectarão até que você atualize seu plano ou reduza o uso. Os dados não serão cobrados ao usar os nós.", - "billingOnlineTimeInfo": "Cobrança de acordo com o tempo em que seus sites permanecem conectados à nuvem. Por exemplo, 44,640 minutos é igual a um site que roda 24/7 para um mês inteiro. Quando você atinge o seu limite, seus sites desconectarão até que você faça o upgrade do seu plano ou reduza o uso. O tempo não é cobrado ao usar nós.", - "billingUsersInfo": "Você será cobrado por cada usuário em sua organização. A cobrança é calculada diariamente com base no número de contas de usuário ativas em sua organização.", - "billingDomainInfo": "Você será cobrado por cada domínio em sua organização. A cobrança é calculada diariamente com base no número de contas de domínio ativas em sua organização.", - "billingRemoteExitNodesInfo": "Você será cobrado por cada Nodo gerenciado em sua organização. A cobrança é calculada diariamente com base no número de Nodos gerenciados ativos em sua organização.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domínio Não Encontrado", "domainNotFoundDescription": "Este recurso está desativado porque o domínio não existe mais em nosso sistema. Defina um novo domínio para este recurso.", "failed": "Falhou", "createNewOrgDescription": "Crie uma nova organização", "organization": "Organização", "port": "Porta", - "securityKeyManage": "Gerir chaves de segurança", + "securityKeyManage": "Gerenciar chaves de segurança", "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", "securityKeyRegister": "Registrar nova chave de segurança", "securityKeyList": "Suas chaves de segurança", @@ -1357,13 +1357,13 @@ "createDomainARecords": "Registros A", "createDomainRecordNumber": "Registrar {number}", "createDomainTxtRecords": "Registros TXT", - "createDomainSaveTheseRecords": "Guardar Esses Registros", + "createDomainSaveTheseRecords": "Salvar Esses Registros", "createDomainSaveTheseRecordsDescription": "Certifique-se de salvar esses registros DNS, pois você não os verá novamente.", "createDomainDnsPropagation": "Propagação DNS", "createDomainDnsPropagationDescription": "Alterações no DNS podem levar algum tempo para se propagar pela internet. Pode levar de alguns minutos a 48 horas, dependendo do seu provedor de DNS e das configurações de TTL.", "resourcePortRequired": "Número da porta é obrigatório para recursos não-HTTP", "resourcePortNotAllowed": "Número da porta não deve ser definido para recursos HTTP", - "billingPricingCalculatorLink": "Calculadora de Preços", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Concordo com", "termsOfService": "os termos de serviço", @@ -1412,41 +1412,41 @@ "addNewTarget": "Adicionar Novo Alvo", "targetsList": "Lista de Alvos", "targetErrorDuplicateTargetFound": "Alvo duplicado encontrado", - "healthCheckHealthy": "Saudável", - "healthCheckUnhealthy": "Não Saudável", - "healthCheckUnknown": "Desconhecido", - "healthCheck": "Verificação de Saúde", - "configureHealthCheck": "Configurar Verificação de Saúde", - "configureHealthCheckDescription": "Configure a monitorização de saúde para {target}", - "enableHealthChecks": "Ativar Verificações de Saúde", - "enableHealthChecksDescription": "Monitore a saúde deste alvo. Você pode monitorar um ponto de extremidade diferente do alvo, se necessário.", - "healthScheme": "Método", - "healthSelectScheme": "Selecione o Método", - "healthCheckPath": "Caminho", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", - "healthPort": "Porta", - "healthCheckPathDescription": "O caminho para verificar o estado de saúde.", - "healthyIntervalSeconds": "Intervalo Saudável", - "unhealthyIntervalSeconds": "Intervalo Não Saudável", - "IntervalSeconds": "Intervalo Saudável", - "timeoutSeconds": "Tempo Limite", - "timeIsInSeconds": "O tempo está em segundos", - "retryAttempts": "Tentativas de Repetição", - "expectedResponseCodes": "Códigos de Resposta Esperados", - "expectedResponseCodesDescription": "Código de status HTTP que indica estado saudável. Se deixado em branco, 200-300 é considerado saudável.", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Cabeçalhos Personalizados", - "customHeadersDescription": "Separados por cabeçalhos da nova linha: Nome do Cabeçalho: valor", - "headersValidationError": "Cabeçalhos devem estar no formato: Nome do Cabeçalho: valor.", - "saveHealthCheck": "Salvar Verificação de Saúde", - "healthCheckSaved": "Verificação de Saúde Salva", - "healthCheckSavedDescription": "Configuração de verificação de saúde salva com sucesso", - "healthCheckError": "Erro de Verificação de Saúde", - "healthCheckErrorDescription": "Ocorreu um erro ao salvar a configuração de verificação de saúde", - "healthCheckPathRequired": "O caminho de verificação de saúde é obrigatório", - "healthCheckMethodRequired": "O método HTTP é obrigatório", - "healthCheckIntervalMin": "O intervalo de verificação deve ser de pelo menos 5 segundos", - "healthCheckTimeoutMin": "O tempo limite deve ser de pelo menos 1 segundo", - "healthCheckRetryMin": "As tentativas de repetição devem ser pelo menos 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Selecionar método HTTP", "domainPickerSubdomainLabel": "Subdomínio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Digite um subdomínio para buscar e selecionar entre os domínios gratuitos disponíveis.", "domainPickerFreeDomains": "Domínios Gratuitos", "domainPickerSearchForAvailableDomains": "Pesquise por domínios disponíveis", - "domainPickerNotWorkSelfHosted": "Nota: Domínios gratuitos fornecidos não estão disponíveis para instâncias auto-hospedadas no momento.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domínio", "resourceEditDomain": "Editar Domínio", "siteName": "Nome do Site", @@ -1481,7 +1481,7 @@ "editInternalResourceDialogSitePort": "Porta do Site", "editInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "editInternalResourceDialogCancel": "Cancelar", - "editInternalResourceDialogSaveResource": "Guardar Recurso", + "editInternalResourceDialogSaveResource": "Salvar Recurso", "editInternalResourceDialogSuccess": "Sucesso", "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno atualizado com sucesso", "editInternalResourceDialogError": "Erro", @@ -1508,7 +1508,7 @@ "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", "createInternalResourceDialogSitePort": "Porta do Site", - "createInternalResourceDialogSitePortDescription": "Use esta porta para aceder o recurso no site quando conectado com um cliente.", + "createInternalResourceDialogSitePortDescription": "Use esta porta para acessar o recurso no site quando conectado com um cliente.", "createInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "createInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "createInternalResourceDialogDestinationPortDescription": "A porta no IP de destino onde o recurso está acessível.", @@ -1532,7 +1532,7 @@ "siteAddress": "Endereço do Site", "siteAddressDescription": "Especificar o endereço IP do host para que os clientes se conectem. Este é o endereço interno do site na rede Pangolin para os clientes endereçarem. Deve estar dentro da sub-rede da Organização.", "autoLoginExternalIdp": "Login Automático com IDP Externo", - "autoLoginExternalIdpDescription": "Redirecionar imediatamente o utilizador para o IDP externo para autenticação.", + "autoLoginExternalIdpDescription": "Redirecionar imediatamente o usuário para o IDP externo para autenticação.", "selectIdp": "Selecionar IDP", "selectIdpPlaceholder": "Escolher um IDP...", "selectIdpRequired": "Por favor, selecione um IDP quando o login automático estiver ativado.", @@ -1543,72 +1543,72 @@ "autoLoginError": "Erro de Login Automático", "autoLoginErrorNoRedirectUrl": "Nenhum URL de redirecionamento recebido do provedor de identidade.", "autoLoginErrorGeneratingUrl": "Falha ao gerar URL de autenticação.", - "remoteExitNodeManageRemoteExitNodes": "Gerenciar Auto-Hospedados", - "remoteExitNodeDescription": "Gerencie os nós para estender sua conectividade de rede", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Buscar nós...", - "remoteExitNodeAdd": "Adicionar node", - "remoteExitNodeErrorDelete": "Erro ao excluir nó", - "remoteExitNodeQuestionRemove": "Tem certeza que deseja remover o nó {selectedNode} da organização?", - "remoteExitNodeMessageRemove": "Uma vez removido, o nó não estará mais acessível.", - "remoteExitNodeMessageConfirm": "Para confirmar, por favor, digite o nome do nó abaixo.", - "remoteExitNodeConfirmDelete": "Confirmar exclusão do nó", - "remoteExitNodeDelete": "Excluir nó", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Criar nó", - "description": "Crie um novo nó para estender sua conectividade de rede", - "viewAllButton": "Ver Todos os Nós", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Estratégia de Criação", - "description": "Escolha isto para configurar o seu nó manualmente ou gerar novas credenciais.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adotar Nodo", - "description": "Escolha isto se você já tem credenciais para o nó." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Gerar Chaves", - "description": "Escolha esta opção se você quer gerar novas chaves para o nó" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adotar Nodo Existente", - "description": "Digite as credenciais do nó existente que deseja adoptar", - "nodeIdLabel": "Nó ID", - "nodeIdDescription": "O ID do nó existente que você deseja adoptar", - "secretLabel": "Chave Secreta", - "secretDescription": "A chave secreta do nó existente", - "submitButton": "Nó Adotado" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Credenciais Geradas", - "description": "Use estas credenciais geradas para configurar o seu nó", - "nodeIdTitle": "Nó ID", - "secretTitle": "Chave Secreta", - "saveCredentialsTitle": "Adicionar Credenciais à Configuração", - "saveCredentialsDescription": "Adicione essas credenciais ao arquivo de configuração do seu nodo de Pangolin auto-hospedado para completar a conexão.", - "submitButton": "Criar nó" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "ID do nó e Segredo são necessários ao adotar um nó existente" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Falha ao carregar padrões", - "defaultsNotLoaded": "Padrões não carregados", - "createFailed": "Falha ao criar nó" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Nó criado com sucesso" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Seleção de nó", - "remoteExitNodeSelectionDescription": "Selecione um nó para encaminhar o tráfego para este site local", - "remoteExitNodeRequired": "Um nó deve ser seleccionado para sites locais", - "noRemoteExitNodesAvailable": "Nenhum nó disponível", - "noRemoteExitNodesAvailableDescription": "Nenhum nó está disponível para esta organização. Crie um nó primeiro para usar sites locais.", - "exitNode": "Nodo de Saída", - "country": "País", - "rulesMatchCountry": "Atualmente baseado no IP de origem", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Gerenciado Auto-Hospedado", "description": "Servidor Pangolin auto-hospedado mais confiável e com baixa manutenção com sinos extras e assobiamentos", @@ -1625,7 +1625,7 @@ }, "benefitLessMaintenance": { "title": "Menos manutenção", - "description": "Sem migrações, backups ou infraestrutura extra para gerir. Lidamos com isso na nuvem." + "description": "Sem migrações, backups ou infraestrutura extra para gerenciar. Lidamos com isso na nuvem." }, "benefitCloudFailover": { "title": "Falha na nuvem", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Domínio Internacional Detectado", "willbestoredas": "Será armazenado como:", - "roleMappingDescription": "Determinar como as funções são atribuídas aos usuários quando eles fazem login quando Auto Provisão está habilitada.", - "selectRole": "Selecione uma função", - "roleMappingExpression": "Expressão", - "selectRolePlaceholder": "Escolha uma função", - "selectRoleDescription": "Selecione uma função para atribuir a todos os usuários deste provedor de identidade", - "roleMappingExpressionDescription": "Insira uma expressão JMESPath para extrair informações da função do token de ID", - "idpTenantIdRequired": "ID do inquilino é necessária", - "invalidValue": "Valor Inválido", - "idpTypeLabel": "Tipo de provedor de identidade", - "roleMappingExpressionPlaceholder": "ex.: Contem (grupos, 'administrador') && 'Administrador' 「'Membro'", - "idpGoogleConfiguration": "Configuração do Google", - "idpGoogleConfigurationDescription": "Configurar suas credenciais do Google OAuth2", - "idpGoogleClientIdDescription": "Seu ID de Cliente OAuth2 do Google", - "idpGoogleClientSecretDescription": "Seu Segredo de Cliente OAuth2 do Google", - "idpAzureConfiguration": "Configuração de ID do Azure Entra", - "idpAzureConfigurationDescription": "Configure as suas credenciais do Azure Entra ID OAuth2", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "seu-tenente-id", - "idpAzureTenantIdDescription": "Seu ID do tenant Azure (encontrado na visão geral do diretório ativo Azure)", - "idpAzureClientIdDescription": "Seu ID de Cliente de Registro do App Azure", - "idpAzureClientSecretDescription": "Seu segredo de cliente de registro de aplicativos Azure", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Configuração do Google", - "idpAzureConfigurationTitle": "Configuração de ID do Azure Entra", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Seu ID de Cliente de Registro do App Azure", - "idpAzureClientSecretDescription2": "Seu segredo de cliente de registro de aplicativos Azure", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Provedor Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Sub-rede", - "subnetDescription": "A sub-rede para a configuração de rede dessa organização.", - "authPage": "Página de Autenticação", - "authPageDescription": "Configurar a página de autenticação para sua organização", - "authPageDomain": "Domínio de Página Autenticação", - "noDomainSet": "Nenhum domínio definido", - "changeDomain": "Alterar domínio", - "selectDomain": "Selecionar domínio", - "restartCertificate": "Reiniciar Certificado", - "editAuthPageDomain": "Editar Página de Autenticação", - "setAuthPageDomain": "Definir domínio da página de autenticação", - "failedToFetchCertificate": "Falha ao buscar o certificado", - "failedToRestartCertificate": "Falha ao reiniciar o certificado", - "addDomainToEnableCustomAuthPages": "Adicione um domínio para habilitar páginas de autenticação personalizadas para sua organização", - "selectDomainForOrgAuthPage": "Selecione um domínio para a página de autenticação da organização", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Domínio fornecido", "domainPickerFreeProvidedDomain": "Domínio fornecido grátis", "domainPickerVerified": "Verificada", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" não pôde ser válido para {domain}.", "domainPickerSubdomainSanitized": "Subdomínio banalizado", "domainPickerSubdomainCorrected": "\"{sub}\" foi corrigido para \"{sanitized}\"", - "orgAuthSignInTitle": "Entrar na sua organização", - "orgAuthChooseIdpDescription": "Escolha o seu provedor de identidade para continuar", - "orgAuthNoIdpConfigured": "Esta organização não tem nenhum provedor de identidade configurado. Você pode entrar com a identidade do seu Pangolin.", - "orgAuthSignInWithPangolin": "Entrar com o Pangolin", - "subscriptionRequiredToUse": "Uma assinatura é necessária para usar esse recurso.", - "idpDisabled": "Provedores de identidade estão desabilitados.", - "orgAuthPageDisabled": "A página de autenticação da organização está desativada.", - "domainRestartedDescription": "Verificação de domínio reiniciado com sucesso", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", - "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui." + "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 38c3c49778c200d8555d0d176f0b1fd2a59d0d2e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:23 -0700 Subject: [PATCH 137/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 363 ++++++++++++++++++++++---------------------- 1 file changed, 184 insertions(+), 179 deletions(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 290d0215..a2508d47 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Простейший способ создать точку входа в вашу сеть. Дополнительная настройка не требуется.", "siteWg": "Базовый WireGuard", "siteWgDescription": "Используйте любой клиент WireGuard для открытия туннеля. Требуется ручная настройка NAT.", - "siteWgDescriptionSaas": "Используйте любой клиент WireGuard для создания туннеля. Требуется ручная настройка NAT. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Только локальные ресурсы. Без туннелирования.", - "siteLocalDescriptionSaas": "Только локальные ресурсы. Без туннелирования. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Просмотреть все сайты", "siteTunnelDescription": "Выберите способ подключения к вашему сайту", "siteNewtCredentials": "Учётные данные Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-ресурс", "resourceHTTPDescription": "Проксирование запросов к вашему приложению через HTTPS с использованием поддомена или базового домена.", "resourceRaw": "Сырой TCP/UDP-ресурс", - "resourceRawDescription": "Проксирование запросов к вашему приложению через TCP/UDP с использованием по номеру порта.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Создание ресурса", "resourceCreateDescription": "Следуйте инструкциям ниже для создания нового ресурса", "resourceSeeAll": "Посмотреть все ресурсы", @@ -168,9 +168,9 @@ "siteSelect": "Выберите сайт", "siteSearch": "Поиск сайта", "siteNotFound": "Сайт не найден.", - "selectCountry": "Выберите страну", - "searchCountries": "Поиск стран...", - "noCountryFound": "Страна не найдена.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Этот сайт предоставит подключение к цели.", "resourceType": "Тип ресурса", "resourceTypeDescription": "Определите, как вы хотите получать доступ к вашему ресурсу", @@ -239,7 +239,7 @@ "accessUserCreate": "Создать пользователя", "accessUserRemove": "Удалить пользователя", "username": "Имя пользователя", - "identityProvider": "Поставщик удостоверений", + "identityProvider": "Identity Provider", "role": "Роль", "nameRequired": "Имя обязательно", "accessRolesManage": "Управление ролями", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Поддомен: {subdomain}", "domainPickerNamespace": "Пространство имен: {namespace}", "domainPickerShowMore": "Показать еще", - "regionSelectorTitle": "Выберите регион", - "regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.", - "regionSelectorPlaceholder": "Выбор региона", - "regionSelectorComingSoon": "Скоро будет", - "billingLoadingSubscription": "Загрузка подписки...", - "billingFreeTier": "Бесплатный уровень", - "billingWarningOverLimit": "Предупреждение: Вы превысили одну или несколько границ использования. Ваши сайты не подключатся, пока вы не измените подписку или не скорректируете использование.", - "billingUsageLimitsOverview": "Обзор лимитов использования", - "billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@fossorial.io.", - "billingDataUsage": "Использование данных", - "billingOnlineTime": "Время работы сайта", - "billingUsers": "Активные пользователи", - "billingDomains": "Активные домены", - "billingRemoteExitNodes": "Активные самоуправляемые узлы", - "billingNoLimitConfigured": "Лимит не установлен", - "billingEstimatedPeriod": "Предполагаемый период выставления счетов", - "billingIncludedUsage": "Включенное использование", - "billingIncludedUsageDescription": "Использование, включенное в ваш текущий план подписки", - "billingFreeTierIncludedUsage": "Бесплатное использование ограничений", - "billingIncluded": "включено", - "billingEstimatedTotal": "Предполагаемая сумма:", - "billingNotes": "Заметки", - "billingEstimateNote": "Это приблизительная оценка на основании вашего текущего использования.", - "billingActualChargesMayVary": "Фактические начисления могут отличаться.", - "billingBilledAtEnd": "С вас будет выставлен счет в конце периода выставления счетов.", - "billingModifySubscription": "Изменить подписку", - "billingStartSubscription": "Начать подписку", - "billingRecurringCharge": "Периодический взнос", - "billingManageSubscriptionSettings": "Управляйте настройками и предпочтениями вашей подписки", - "billingNoActiveSubscription": "У вас нет активной подписки. Начните подписку, чтобы увеличить лимиты использования.", - "billingFailedToLoadSubscription": "Не удалось загрузить подписку", - "billingFailedToLoadUsage": "Не удалось загрузить использование", - "billingFailedToGetCheckoutUrl": "Не удалось получить URL-адрес для оплаты", - "billingPleaseTryAgainLater": "Пожалуйста, повторите попытку позже.", - "billingCheckoutError": "Ошибка при оформлении заказа", - "billingFailedToGetPortalUrl": "Не удалось получить URL-адрес портала", - "billingPortalError": "Ошибка портала", - "billingDataUsageInfo": "Вы несете ответственность за все данные, переданные через безопасные туннели при подключении к облаку. Это включает как входящий, так и исходящий трафик на всех ваших сайтах. При достижении лимита ваши сайты будут отключаться до тех пор, пока вы не обновите план или не уменьшите его использование. При использовании узлов не взимается плата.", - "billingOnlineTimeInfo": "Вы тарифицируете на то, как долго ваши сайты будут подключены к облаку. Например, 44 640 минут равны одному сайту, работающему круглосуточно за весь месяц. Когда вы достигните лимита, ваши сайты будут отключаться до тех пор, пока вы не обновите тарифный план или не сократите нагрузку. При использовании узлов не тарифицируется.", - "billingUsersInfo": "С вас взимается плата за каждого пользователя в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей пользователей в вашей организации.", - "billingDomainInfo": "С вас взимается плата за каждый домен в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей доменов в вашей организации.", - "billingRemoteExitNodesInfo": "С вас взимается плата за каждый управляемый узел в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных управляемых узлов в вашей организации.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Домен не найден", "domainNotFoundDescription": "Этот ресурс отключен, так как домен больше не существует в нашей системе. Пожалуйста, установите новый домен для этого ресурса.", "failed": "Ошибка", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Изменения DNS могут занять некоторое время для распространения через интернет. Это может занять от нескольких минут до 48 часов в зависимости от вашего DNS провайдера и настроек TTL.", "resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов", "resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов", - "billingPricingCalculatorLink": "Калькулятор расценок", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Я согласен с", "termsOfService": "условия использования", @@ -1412,41 +1412,41 @@ "addNewTarget": "Добавить новую цель", "targetsList": "Список целей", "targetErrorDuplicateTargetFound": "Обнаружена дублирующаяся цель", - "healthCheckHealthy": "Здоровый", - "healthCheckUnhealthy": "Нездоровый", - "healthCheckUnknown": "Неизвестно", - "healthCheck": "Проверка здоровья", - "configureHealthCheck": "Настроить проверку здоровья", - "configureHealthCheckDescription": "Настройте мониторинг состояния для {target}", - "enableHealthChecks": "Включить проверки здоровья", - "enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.", - "healthScheme": "Метод", - "healthSelectScheme": "Выберите метод", - "healthCheckPath": "Путь", - "healthHostname": "IP / хост", - "healthPort": "Порт", - "healthCheckPathDescription": "Путь к проверке состояния здоровья.", - "healthyIntervalSeconds": "Интервал здоровых состояний", - "unhealthyIntervalSeconds": "Интервал нездоровых состояний", - "IntervalSeconds": "Интервал здоровых состояний", - "timeoutSeconds": "Тайм-аут", - "timeIsInSeconds": "Время указано в секундах", - "retryAttempts": "Количество попыток повторного запроса", - "expectedResponseCodes": "Ожидаемые коды ответов", - "expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Пользовательские заголовки", - "customHeadersDescription": "Заголовки новой строки, разделённые: название заголовка: значение", - "headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.", - "saveHealthCheck": "Сохранить проверку здоровья", - "healthCheckSaved": "Проверка здоровья сохранена", - "healthCheckSavedDescription": "Конфигурация проверки состояния успешно сохранена", - "healthCheckError": "Ошибка проверки состояния", - "healthCheckErrorDescription": "Произошла ошибка при сохранении конфигурации проверки состояния", - "healthCheckPathRequired": "Требуется путь проверки состояния", - "healthCheckMethodRequired": "Требуется метод HTTP", - "healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд", - "healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды", - "healthCheckRetryMin": "Количество попыток должно быть не менее 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP метод", "selectHttpMethod": "Выберите HTTP метод", "domainPickerSubdomainLabel": "Поддомен", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Введите поддомен для поиска и выбора из доступных свободных доменов.", "domainPickerFreeDomains": "Свободные домены", "domainPickerSearchForAvailableDomains": "Поиск доступных доменов", - "domainPickerNotWorkSelfHosted": "Примечание: бесплатные предоставляемые домены в данный момент недоступны для самоуправляемых экземпляров.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Домен", "resourceEditDomain": "Редактировать домен", "siteName": "Имя сайта", @@ -1543,72 +1543,72 @@ "autoLoginError": "Ошибка автоматического входа", "autoLoginErrorNoRedirectUrl": "URL-адрес перенаправления не получен от провайдера удостоверения.", "autoLoginErrorGeneratingUrl": "Не удалось сгенерировать URL-адрес аутентификации.", - "remoteExitNodeManageRemoteExitNodes": "Управление самоуправляемым", - "remoteExitNodeDescription": "Управляйте узлами для расширения сетевого подключения", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Поиск узлов...", - "remoteExitNodeAdd": "Добавить узел", - "remoteExitNodeErrorDelete": "Ошибка удаления узла", - "remoteExitNodeQuestionRemove": "Вы уверены, что хотите удалить узел {selectedNode} из организации?", - "remoteExitNodeMessageRemove": "После удаления узел больше не будет доступен.", - "remoteExitNodeMessageConfirm": "Для подтверждения введите имя узла ниже.", - "remoteExitNodeConfirmDelete": "Подтвердите удаление узла", - "remoteExitNodeDelete": "Удалить узел", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Создать узел", - "description": "Создайте новый узел, чтобы расширить сетевое подключение", - "viewAllButton": "Все узлы", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Стратегия создания", - "description": "Выберите эту опцию для настройки вашего узла или создания новых учетных данных.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Принять узел", - "description": "Выберите это, если у вас уже есть учетные данные для узла." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Сгенерировать ключи", - "description": "Выберите это, если вы хотите создать новые ключи для узла" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Принять существующий узел", - "description": "Введите учетные данные существующего узла, который вы хотите принять", - "nodeIdLabel": "ID узла", - "nodeIdDescription": "ID существующего узла, который вы хотите принять", - "secretLabel": "Секретный ключ", - "secretDescription": "Секретный ключ существующего узла", - "submitButton": "Принять узел" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Сгенерированные учетные данные", - "description": "Используйте эти учётные данные для настройки вашего узла", - "nodeIdTitle": "ID узла", - "secretTitle": "Секретный ключ", - "saveCredentialsTitle": "Добавить учетные данные в конфигурацию", - "saveCredentialsDescription": "Добавьте эти учетные данные в файл конфигурации вашего самоуправляемого узла Pangolin, чтобы завершить подключение.", - "submitButton": "Создать узел" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "ID узла и секрет требуются при установке существующего узла" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Не удалось загрузить параметры по умолчанию", - "defaultsNotLoaded": "Параметры по умолчанию не загружены", - "createFailed": "Не удалось создать узел" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Узел успешно создан" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Выбор узла", - "remoteExitNodeSelectionDescription": "Выберите узел для маршрутизации трафика для этого локального сайта", - "remoteExitNodeRequired": "Узел должен быть выбран для локальных сайтов", - "noRemoteExitNodesAvailable": "Нет доступных узлов", - "noRemoteExitNodesAvailableDescription": "Для этой организации узлы не доступны. Сначала создайте узел, чтобы использовать локальные сайты.", - "exitNode": "Узел выхода", - "country": "Страна", - "rulesMatchCountry": "В настоящее время основано на исходном IP", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Управляемый с самовывоза", "description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Обнаружен международный домен", "willbestoredas": "Будет храниться как:", - "roleMappingDescription": "Определите, как роли, назначаемые пользователям, когда они войдут в систему автоматического профиля.", - "selectRole": "Выберите роль", - "roleMappingExpression": "Выражение", - "selectRolePlaceholder": "Выберите роль", - "selectRoleDescription": "Выберите роль, чтобы назначить всем пользователям этого поставщика идентификации", - "roleMappingExpressionDescription": "Введите выражение JMESPath, чтобы извлечь информацию о роли из ID токена", - "idpTenantIdRequired": "Требуется ID владельца", - "invalidValue": "Неверное значение", - "idpTypeLabel": "Тип поставщика удостоверений", - "roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Конфигурация Google", - "idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2", - "idpGoogleClientIdDescription": "Ваш Google OAuth2 ID клиента", - "idpGoogleClientSecretDescription": "Ваш Google OAuth2 Секрет", - "idpAzureConfiguration": "Конфигурация Azure Entra ID", - "idpAzureConfigurationDescription": "Настройте учетные данные Azure Entra ID OAuth2", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "ваш тенант-id", - "idpAzureTenantIdDescription": "Идентификатор арендатора Azure (найден в обзоре Active Directory Azure)", - "idpAzureClientIdDescription": "Ваш идентификатор клиента Azure App", - "idpAzureClientSecretDescription": "Секрет регистрации клиента Azure App", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Конфигурация Google", - "idpAzureConfigurationTitle": "Конфигурация Azure Entra ID", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Ваш идентификатор клиента Azure App", - "idpAzureClientSecretDescription2": "Секрет регистрации клиента Azure App", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC провайдер", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Подсеть", - "subnetDescription": "Подсеть для конфигурации сети этой организации.", - "authPage": "Страница авторизации", - "authPageDescription": "Настройка страницы авторизации для вашей организации", - "authPageDomain": "Домен страницы авторизации", - "noDomainSet": "Домен не установлен", - "changeDomain": "Изменить домен", - "selectDomain": "Выберите домен", - "restartCertificate": "Перезапустить сертификат", - "editAuthPageDomain": "Редактировать домен страницы авторизации", - "setAuthPageDomain": "Установить домен страницы авторизации", - "failedToFetchCertificate": "Не удалось получить сертификат", - "failedToRestartCertificate": "Не удалось перезапустить сертификат", - "addDomainToEnableCustomAuthPages": "Добавьте домен для включения пользовательских страниц аутентификации для вашей организации", - "selectDomainForOrgAuthPage": "Выберите домен для страницы аутентификации организации", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Домен предоставлен", "domainPickerFreeProvidedDomain": "Бесплатный домен", "domainPickerVerified": "Подтверждено", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не может быть действительным для {domain}.", "domainPickerSubdomainSanitized": "Субдомен очищен", "domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"", - "orgAuthSignInTitle": "Войдите в свою организацию", - "orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения", - "orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.", - "orgAuthSignInWithPangolin": "Войти через Pangolin", - "subscriptionRequiredToUse": "Для использования этой функции требуется подписка.", - "idpDisabled": "Провайдеры идентификации отключены.", - "orgAuthPageDisabled": "Страница авторизации организации отключена.", - "domainRestartedDescription": "Проверка домена успешно перезапущена", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", - "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда." + "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 71ba98075777e0742de67f4c51412f36ee6a37f0 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:24 -0700 Subject: [PATCH 138/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 369 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 182 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 33c37327..62991e05 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -94,9 +94,9 @@ "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.", - "siteWgDescriptionSaas": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Yalnızca yerel kaynaklar. Tünelleme yok.", - "siteLocalDescriptionSaas": "Yalnızca yerel kaynaklar. Tünel yok. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Tüm Siteleri Gör", "siteTunnelDescription": "Sitenize nasıl bağlanmak istediğinizi belirleyin", "siteNewtCredentials": "Newt Kimlik Bilgileri", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS Kaynağı", "resourceHTTPDescription": "Bir alt alan adı veya temel alan adı kullanarak uygulamanıza HTTPS üzerinden vekil istek gönderin.", "resourceRaw": "Ham TCP/UDP Kaynağı", - "resourceRawDescription": "Uygulamanıza TCP/UDP üzerinden port numarası ile vekil istek gönderin.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Kaynak Oluştur", "resourceCreateDescription": "Yeni bir kaynak oluşturmak için aşağıdaki adımları izleyin", "resourceSeeAll": "Tüm Kaynakları Gör", @@ -168,9 +168,9 @@ "siteSelect": "Site seç", "siteSearch": "Site ara", "siteNotFound": "Herhangi bir site bulunamadı.", - "selectCountry": "Ülke Seç", - "searchCountries": "Ülkeleri ara...", - "noCountryFound": "Ülke bulunamadı.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Bu site hedefe bağlantı sağlayacaktır.", "resourceType": "Kaynak Türü", "resourceTypeDescription": "Kaynağınıza nasıl erişmek istediğinizi belirleyin", @@ -817,7 +817,7 @@ "redirectUrl": "Yönlendirme URL'si", "redirectUrlAbout": "Yönlendirme URL'si Hakkında", "redirectUrlAboutDescription": "Bu, kimlik doğrulamasından sonra kullanıcıların yönlendirileceği URL'dir. Bu URL'yi kimlik sağlayıcınızın ayarlarında yapılandırmanız gerekir.", - "pangolinAuth": "Yetkilendirme - Pangolin", + "pangolinAuth": "Auth - Pangolin", "verificationCodeLengthRequirements": "Doğrulama kodunuz 8 karakter olmalıdır.", "errorOccurred": "Bir hata oluştu", "emailErrorVerify": "E-posta doğrulanamadı: ", @@ -1242,7 +1242,7 @@ "sidebarExpand": "Genişlet", "newtUpdateAvailable": "Güncelleme Mevcut", "newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.", - "domainPickerEnterDomain": "Alan Adı", + "domainPickerEnterDomain": "Domain", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.", "domainPickerDescriptionSaas": "Mevcut seçenekleri görmek için tam etki alanı, alt etki alanı veya sadece bir isim girin", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Alt Alan: {subdomain}", "domainPickerNamespace": "Ad Alanı: {namespace}", "domainPickerShowMore": "Daha Fazla Göster", - "regionSelectorTitle": "Bölge Seç", - "regionSelectorInfo": "Bir bölge seçmek, konumunuz için daha iyi performans sağlamamıza yardımcı olur. Sunucunuzla aynı bölgede olmanıza gerek yoktur.", - "regionSelectorPlaceholder": "Bölge Seçin", - "regionSelectorComingSoon": "Yakında Geliyor", - "billingLoadingSubscription": "Abonelik yükleniyor...", - "billingFreeTier": "Ücretsiz Dilim", - "billingWarningOverLimit": "Uyarı: Bir veya daha fazla kullanım limitini aştınız. Aboneliğinizi değiştirmediğiniz veya kullanımı ayarlamadığınız sürece siteleriniz bağlanmayacaktır.", - "billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü", - "billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@fossorial.io adresinden bizimle iletişime geçin.", - "billingDataUsage": "Veri Kullanımı", - "billingOnlineTime": "Site Çevrimiçi Süresi", - "billingUsers": "Aktif Kullanıcılar", - "billingDomains": "Aktif Alanlar", - "billingRemoteExitNodes": "Aktif Öz-Host Düğümleri", - "billingNoLimitConfigured": "Hiçbir limit yapılandırılmadı", - "billingEstimatedPeriod": "Tahmini Fatura Dönemi", - "billingIncludedUsage": "Dahil Kullanım", - "billingIncludedUsageDescription": "Mevcut abonelik planınıza bağlı kullanım", - "billingFreeTierIncludedUsage": "Ücretsiz dilim kullanım hakları", - "billingIncluded": "dahil", - "billingEstimatedTotal": "Tahmini Toplam:", - "billingNotes": "Notlar", - "billingEstimateNote": "Bu, mevcut kullanımınıza dayalı bir tahmindir.", - "billingActualChargesMayVary": "Asıl ücretler farklılık gösterebilir.", - "billingBilledAtEnd": "Fatura döneminin sonunda fatura düzenlenecektir.", - "billingModifySubscription": "Aboneliği Düzenle", - "billingStartSubscription": "Aboneliği Başlat", - "billingRecurringCharge": "Yinelenen Ücret", - "billingManageSubscriptionSettings": "Abonelik ayarlarınızı ve tercihlerinizi yönetin", - "billingNoActiveSubscription": "Aktif bir aboneliğiniz yok. Kullanım limitlerini artırmak için aboneliğinizi başlatın.", - "billingFailedToLoadSubscription": "Abonelik yüklenemedi", - "billingFailedToLoadUsage": "Kullanım yüklenemedi", - "billingFailedToGetCheckoutUrl": "Ödeme URL'si alınamadı", - "billingPleaseTryAgainLater": "Lütfen daha sonra tekrar deneyin.", - "billingCheckoutError": "Ödeme Hatası", - "billingFailedToGetPortalUrl": "Portal URL'si alınamadı", - "billingPortalError": "Portal Hatası", - "billingDataUsageInfo": "Buluta bağlandığınızda, güvenli tünellerinizden aktarılan tüm verilerden ücret alınırsınız. Bu, tüm sitelerinizdeki gelen ve giden trafiği içerir. Limitinize ulaştığınızda, planınızı yükseltmeli veya kullanımı azaltmalısınız, aksi takdirde siteleriniz bağlantıyı keser. Düğümler kullanırken verilerden ücret alınmaz.", - "billingOnlineTimeInfo": "Sitelerinizin buluta ne kadar süre bağlı kaldığına göre ücretlendirilirsiniz. Örneğin, 44,640 dakika, bir sitenin 24/7 boyunca tam bir ay boyunca çalışması anlamına gelir. Limitinize ulaştığınızda, planınızı yükseltmeyip kullanımı azaltmazsanız siteleriniz bağlantıyı keser. Düğümler kullanırken zamandan ücret alınmaz.", - "billingUsersInfo": "Kuruluşunuzdaki her kullanıcı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif kullanıcı hesaplarının sayısına göre günlük olarak hesaplanır.", - "billingDomainInfo": "Kuruluşunuzdaki her alan adı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif alan adları hesaplarının sayısına göre günlük olarak hesaplanır.", - "billingRemoteExitNodesInfo": "Kuruluşunuzdaki her yönetilen Düğüm için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif yönetilen Düğümler sayısına göre günlük olarak hesaplanır.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Alan Adı Bulunamadı", "domainNotFoundDescription": "Bu kaynak devre dışıdır çünkü alan adı sistemimizde artık mevcut değil. Bu kaynak için yeni bir alan adı belirleyin.", "failed": "Başarısız", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS değişikliklerinin internet genelinde yayılması zaman alabilir. DNS sağlayıcınız ve TTL ayarlarına bağlı olarak bu birkaç dakika ile 48 saat arasında değişebilir.", "resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir", "resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı", - "billingPricingCalculatorLink": "Fiyat Hesaplayıcı", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Kabul ediyorum", "termsOfService": "hizmet şartları", @@ -1412,41 +1412,41 @@ "addNewTarget": "Yeni Hedef Ekle", "targetsList": "Hedefler Listesi", "targetErrorDuplicateTargetFound": "Yinelenen hedef bulundu", - "healthCheckHealthy": "Sağlıklı", - "healthCheckUnhealthy": "Sağlıksız", - "healthCheckUnknown": "Bilinmiyor", - "healthCheck": "Sağlık Kontrolü", - "configureHealthCheck": "Sağlık Kontrolünü Yapılandır", - "configureHealthCheckDescription": "{hedef} için sağlık izleme kurun", - "enableHealthChecks": "Sağlık Kontrollerini Etkinleştir", - "enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.", - "healthScheme": "Yöntem", - "healthSelectScheme": "Yöntem Seç", - "healthCheckPath": "Yol", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "Sağlık durumunu kontrol etmek için yol.", - "healthyIntervalSeconds": "Sağlıklı Aralık", - "unhealthyIntervalSeconds": "Sağlıksız Aralık", - "IntervalSeconds": "Sağlıklı Aralık", - "timeoutSeconds": "Zaman Aşımı", - "timeIsInSeconds": "Zaman saniye cinsindendir", - "retryAttempts": "Tekrar Deneme Girişimleri", - "expectedResponseCodes": "Beklenen Yanıt Kodları", - "expectedResponseCodesDescription": "Sağlıklı durumu gösteren HTTP durum kodu. Boş bırakılırsa, 200-300 arası sağlıklı kabul edilir.", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Özel Başlıklar", - "customHeadersDescription": "Başlıklar yeni satırla ayrılmış: Başlık-Adı: değer", - "headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.", - "saveHealthCheck": "Sağlık Kontrolünü Kaydet", - "healthCheckSaved": "Sağlık Kontrolü Kaydedildi", - "healthCheckSavedDescription": "Sağlık kontrol yapılandırması başarıyla kaydedildi", - "healthCheckError": "Sağlık Kontrol Hatası", - "healthCheckErrorDescription": "Sağlık kontrol yapılandırması kaydedilirken bir hata oluştu", - "healthCheckPathRequired": "Sağlık kontrol yolu gereklidir", - "healthCheckMethodRequired": "HTTP yöntemi gereklidir", - "healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır", - "healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır", - "healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP Yöntemi", "selectHttpMethod": "HTTP yöntemini seçin", "domainPickerSubdomainLabel": "Alt Alan Adı", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Mevcut ücretsiz alan adları arasından aramak ve seçmek için bir alt alan adı girin.", "domainPickerFreeDomains": "Ücretsiz Alan Adları", "domainPickerSearchForAvailableDomains": "Mevcut alan adlarını ara", - "domainPickerNotWorkSelfHosted": "Not: Ücretsiz sağlanan alan adları şu anda öz-host edilmiş örnekler için kullanılabilir değildir.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Alan Adı", "resourceEditDomain": "Alan Adını Düzenle", "siteName": "Site Adı", @@ -1543,72 +1543,72 @@ "autoLoginError": "Otomatik Giriş Hatası", "autoLoginErrorNoRedirectUrl": "Kimlik sağlayıcıdan yönlendirme URL'si alınamadı.", "autoLoginErrorGeneratingUrl": "Kimlik doğrulama URL'si oluşturulamadı.", - "remoteExitNodeManageRemoteExitNodes": "Öz-Host Yönetim", - "remoteExitNodeDescription": "Ağ bağlantınızı genişletmek için düğümleri yönetin", - "remoteExitNodes": "Düğümler", - "searchRemoteExitNodes": "Düğüm ara...", - "remoteExitNodeAdd": "Düğüm Ekle", - "remoteExitNodeErrorDelete": "Düğüm silinirken hata oluştu", - "remoteExitNodeQuestionRemove": "{selectedNode} düğümünü organizasyondan kaldırmak istediğinizden emin misiniz?", - "remoteExitNodeMessageRemove": "Kaldırıldığında, düğüm artık erişilebilir olmayacaktır.", - "remoteExitNodeMessageConfirm": "Onaylamak için lütfen aşağıya düğümün adını yazın.", - "remoteExitNodeConfirmDelete": "Düğüm Silmeyi Onayla", - "remoteExitNodeDelete": "Düğümü Sil", - "sidebarRemoteExitNodes": "Düğümler", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodes": "Nodes", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", + "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Düğüm Oluştur", - "description": "Ağ bağlantınızı genişletmek için yeni bir düğüm oluşturun", - "viewAllButton": "Tüm Düğümleri Gör", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Oluşturma Stratejisi", - "description": "Düğümünüzü manuel olarak yapılandırmak veya yeni kimlik bilgileri oluşturmak için bunu seçin.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Düğüm Benimse", - "description": "Zaten düğüm için kimlik bilgilerine sahipseniz bunu seçin." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Anahtarları Oluştur", - "description": "Düğüm için yeni anahtarlar oluşturmak istiyorsanız bunu seçin" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Mevcut Düğümü Benimse", - "description": "Adayacağınız mevcut düğümün kimlik bilgilerini girin", - "nodeIdLabel": "Düğüm ID", - "nodeIdDescription": "Adayacağınız mevcut düğümün ID'si", - "secretLabel": "Gizli", - "secretDescription": "Mevcut düğümün gizli anahtarı", - "submitButton": "Düğümü Benimse" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Oluşturulan Kimlik Bilgileri", - "description": "Düğümünüzü yapılandırmak için oluşturulan bu kimlik bilgilerini kullanın", - "nodeIdTitle": "Düğüm ID", - "secretTitle": "Gizli", - "saveCredentialsTitle": "Kimlik Bilgilerini Yapılandırmaya Ekle", - "saveCredentialsDescription": "Bağlantıyı tamamlamak için bu kimlik bilgilerini öz-host Pangolin düğüm yapılandırma dosyanıza ekleyin.", - "submitButton": "Düğüm Oluştur" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "Mevcut bir düğümü benimserken Düğüm ID ve Gizli anahtar gereklidir" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Varsayılanlar yüklenemedi", - "defaultsNotLoaded": "Varsayılanlar yüklenmedi", - "createFailed": "Düğüm oluşturulamadı" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Düğüm başarıyla oluşturuldu" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Düğüm Seçimi", - "remoteExitNodeSelectionDescription": "Yerel site için trafiği yönlendirecek düğümü seçin", - "remoteExitNodeRequired": "Yerel siteler için bir düğüm seçilmelidir", - "noRemoteExitNodesAvailable": "Düğüm Bulunamadı", - "noRemoteExitNodesAvailableDescription": "Bu organizasyon için düğüm mevcut değil. Yerel siteleri kullanmak için önce bir düğüm oluşturun.", - "exitNode": "Çıkış Düğümü", - "country": "Ülke", - "rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Yönetilen Self-Hosted", "description": "Daha güvenilir ve düşük bakım gerektiren, ekstra özelliklere sahip kendi kendine barındırabileceğiniz Pangolin sunucusu", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Uluslararası Alan Adı Tespit Edildi", "willbestoredas": "Şu şekilde depolanacak:", - "roleMappingDescription": "Otomatik Sağlama etkinleştirildiğinde kullanıcıların oturum açarken rollerin nasıl atandığını belirleyin.", - "selectRole": "Bir Rol Seçin", - "roleMappingExpression": "İfade", - "selectRolePlaceholder": "Bir rol seçin", - "selectRoleDescription": "Bu kimlik sağlayıcısından tüm kullanıcılara atanacak bir rol seçin", - "roleMappingExpressionDescription": "Rol bilgilerini ID tokeninden çıkarmak için bir JMESPath ifadesi girin", - "idpTenantIdRequired": "Kiracı Kimliği gereklidir", - "invalidValue": "Geçersiz değer", - "idpTypeLabel": "Kimlik Sağlayıcı Türü", - "roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'", - "idpGoogleConfiguration": "Google Yapılandırması", - "idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın", - "idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz", - "idpGoogleClientSecretDescription": "Google OAuth2 İstemci Sırrınız", - "idpAzureConfiguration": "Azure Entra ID Yapılandırması", - "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 kimlik bilgilerinizi yapılandırın", - "idpTenantId": "Kiracı Kimliği", - "idpTenantIdPlaceholder": "kiraci-kimliginiz", - "idpAzureTenantIdDescription": "Azure kiracı kimliğiniz (Azure Active Directory genel bakışında bulunur)", - "idpAzureClientIdDescription": "Azure Uygulama Kaydı İstemci Kimliğiniz", - "idpAzureClientSecretDescription": "Azure Uygulama Kaydı İstemci Sırrınız", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", + "idpTenantId": "Tenant ID", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Yapılandırması", - "idpAzureConfigurationTitle": "Azure Entra ID Yapılandırması", - "idpTenantIdLabel": "Kiracı Kimliği", - "idpAzureClientIdDescription2": "Azure Uygulama Kaydı İstemci Kimliğiniz", - "idpAzureClientSecretDescription2": "Azure Uygulama Kaydı İstemci Sırrınız", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", + "idpTenantIdLabel": "Tenant ID", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı", - "subnet": "Alt ağ", - "subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.", - "authPage": "Yetkilendirme Sayfası", - "authPageDescription": "Kuruluşunuz için yetkilendirme sayfasını yapılandırın", - "authPageDomain": "Yetkilendirme Sayfası Alanı", - "noDomainSet": "Alan belirlenmedi", - "changeDomain": "Alanı Değiştir", - "selectDomain": "Alan Seçin", - "restartCertificate": "Sertifikayı Yenile", - "editAuthPageDomain": "Yetkilendirme Sayfası Alanını Düzenle", - "setAuthPageDomain": "Yetkilendirme Sayfası Alanını Ayarla", - "failedToFetchCertificate": "Sertifika getirilemedi", - "failedToRestartCertificate": "Sertifika yeniden başlatılamadı", - "addDomainToEnableCustomAuthPages": "Kuruluşunuz için özel kimlik doğrulama sayfalarını etkinleştirmek için bir alan ekleyin", - "selectDomainForOrgAuthPage": "Kuruluşun kimlik doğrulama sayfası için bir alan seçin", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Sağlanan Alan Adı", "domainPickerFreeProvidedDomain": "Ücretsiz Sağlanan Alan Adı", "domainPickerVerified": "Doğrulandı", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" {domain} için geçerli yapılamadı.", "domainPickerSubdomainSanitized": "Alt alan adı temizlendi", "domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi", - "orgAuthSignInTitle": "Kuruluşunuza giriş yapın", - "orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin", - "orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.", - "orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap", - "subscriptionRequiredToUse": "Bu özelliği kullanmak için abonelik gerekmektedir.", - "idpDisabled": "Kimlik sağlayıcılar devre dışı bırakılmıştır.", - "orgAuthPageDisabled": "Kuruluş kimlik doğrulama sayfası devre dışı bırakılmıştır.", - "domainRestartedDescription": "Alan doğrulaması başarıyla yeniden başlatıldı", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", - "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün." + "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From c8bddd42896c886f18595aa5d8a60324e1385081 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:25 -0700 Subject: [PATCH 139/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 367 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 181 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 5708f976..8314f092 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", "siteWg": "基本 WireGuard", "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", - "siteWgDescriptionSaas": "使用任何WireGuard客户端建立隧道。需要手动配置NAT。仅适用于自托管节点。", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "仅限本地资源。不需要隧道。", - "siteLocalDescriptionSaas": "仅本地资源。无需隧道。仅适用于自托管节点。", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", "siteNewtCredentials": "Newt 凭据", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS 资源", "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", "resourceRaw": "TCP/UDP 资源", - "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", "resourceSeeAll": "查看所有资源", @@ -168,9 +168,9 @@ "siteSelect": "选择站点", "siteSearch": "搜索站点", "siteNotFound": "未找到站点。", - "selectCountry": "选择国家", - "searchCountries": "搜索国家...", - "noCountryFound": "找不到国家。", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "此站点将为目标提供连接。", "resourceType": "资源类型", "resourceTypeDescription": "确定如何访问您的资源", @@ -598,7 +598,7 @@ "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", "newtEndpoint": "Newt 端点", - "newtId": "Newt ID", + "newtId": "Newt ID", "newtSecretKey": "Newt 私钥", "architecture": "架构", "sites": "站点", @@ -1156,7 +1156,7 @@ "containerLabels": "标签", "containerLabelsCount": "{count, plural, other {# 标签}}", "containerLabelsTitle": "容器标签", - "containerLabelEmpty": "<为空>", + "containerLabelEmpty": "", "containerPorts": "端口", "containerPortsMore": "+{count} 更多", "containerActions": "行动", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "子域:{subdomain}", "domainPickerNamespace": "命名空间:{namespace}", "domainPickerShowMore": "显示更多", - "regionSelectorTitle": "选择区域", - "regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。", - "regionSelectorPlaceholder": "选择一个区域", - "regionSelectorComingSoon": "即将推出", - "billingLoadingSubscription": "正在加载订阅...", - "billingFreeTier": "免费层", - "billingWarningOverLimit": "警告:您已超出一个或多个使用限制。在您修改订阅或调整使用情况之前,您的站点将无法连接。", - "billingUsageLimitsOverview": "使用限制概览", - "billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@fossorial.io。", - "billingDataUsage": "数据使用情况", - "billingOnlineTime": "站点在线时间", - "billingUsers": "活跃用户", - "billingDomains": "活跃域", - "billingRemoteExitNodes": "活跃自托管节点", - "billingNoLimitConfigured": "未配置限制", - "billingEstimatedPeriod": "估计结算周期", - "billingIncludedUsage": "包含的使用量", - "billingIncludedUsageDescription": "您当前订阅计划中包含的使用量", - "billingFreeTierIncludedUsage": "免费层使用额度", - "billingIncluded": "包含", - "billingEstimatedTotal": "预计总额:", - "billingNotes": "备注", - "billingEstimateNote": "这是根据您当前使用情况的估算。", - "billingActualChargesMayVary": "实际费用可能会有变化。", - "billingBilledAtEnd": "您将在结算周期结束时被计费。", - "billingModifySubscription": "修改订阅", - "billingStartSubscription": "开始订阅", - "billingRecurringCharge": "周期性收费", - "billingManageSubscriptionSettings": "管理您的订阅设置和偏好", - "billingNoActiveSubscription": "您没有活跃的订阅。开始订阅以增加使用限制。", - "billingFailedToLoadSubscription": "无法加载订阅", - "billingFailedToLoadUsage": "无法加载使用情况", - "billingFailedToGetCheckoutUrl": "无法获取结账网址", - "billingPleaseTryAgainLater": "请稍后再试。", - "billingCheckoutError": "结账错误", - "billingFailedToGetPortalUrl": "无法获取门户网址", - "billingPortalError": "门户错误", - "billingDataUsageInfo": "当连接到云端时,您将为通过安全隧道传输的所有数据收取费用。 这包括您所有站点的进出流量。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取数据。", - "billingOnlineTimeInfo": "您要根据您的网站连接到云端的时间长短收取费用。 例如,44,640分钟等于一个24/7全月运行的网站。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取费用。", - "billingUsersInfo": "根据您组织中的活跃用户数量收费。按日计算账单。", - "billingDomainInfo": "根据组织中活跃域的数量收费。按日计算账单。", - "billingRemoteExitNodesInfo": "根据您组织中已管理节点的数量收费。按日计算账单。", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "域未找到", "domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。", "failed": "失败", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 更改可能需要一些时间才能在互联网上传播。这可能需要从几分钟到 48 小时,具体取决于您的 DNS 提供商和 TTL 设置。", "resourcePortRequired": "非 HTTP 资源必须输入端口号", "resourcePortNotAllowed": "HTTP 资源不应设置端口号", - "billingPricingCalculatorLink": "价格计算器", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "我同意", "termsOfService": "服务条款", @@ -1390,7 +1390,7 @@ "clientOlmCredentials": "Olm 凭据", "clientOlmCredentialsDescription": "这是 Olm 服务器的身份验证方式", "olmEndpoint": "Olm 端点", - "olmId": "Olm ID", + "olmId": "Olm ID", "olmSecretKey": "Olm 私钥", "clientCredentialsSave": "保存您的凭据", "clientCredentialsSaveDescription": "该信息仅会显示一次,请确保将其复制到安全位置。", @@ -1412,41 +1412,41 @@ "addNewTarget": "添加新目标", "targetsList": "目标列表", "targetErrorDuplicateTargetFound": "找到重复的目标", - "healthCheckHealthy": "正常", - "healthCheckUnhealthy": "不正常", - "healthCheckUnknown": "未知", - "healthCheck": "健康检查", - "configureHealthCheck": "配置健康检查", - "configureHealthCheckDescription": "为 {target} 设置健康监控", - "enableHealthChecks": "启用健康检查", - "enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。", - "healthScheme": "方法", - "healthSelectScheme": "选择方法", - "healthCheckPath": "路径", - "healthHostname": "IP / 主机", - "healthPort": "端口", - "healthCheckPathDescription": "用于检查健康状态的路径。", - "healthyIntervalSeconds": "正常间隔", - "unhealthyIntervalSeconds": "不正常间隔", - "IntervalSeconds": "正常间隔", - "timeoutSeconds": "超时", - "timeIsInSeconds": "时间以秒为单位", - "retryAttempts": "重试次数", - "expectedResponseCodes": "期望响应代码", - "expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空,200-300 被视为健康。", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "自定义标题", - "customHeadersDescription": "头部新行分隔:头部名称:值", - "headersValidationError": "头部必须是格式:头部名称:值。", - "saveHealthCheck": "保存健康检查", - "healthCheckSaved": "健康检查已保存", - "healthCheckSavedDescription": "健康检查配置已成功保存。", - "healthCheckError": "健康检查错误", - "healthCheckErrorDescription": "保存健康检查配置时出错", - "healthCheckPathRequired": "健康检查路径为必填项", - "healthCheckMethodRequired": "HTTP 方法为必填项", - "healthCheckIntervalMin": "检查间隔必须至少为 5 秒", - "healthCheckTimeoutMin": "超时必须至少为 1 秒", - "healthCheckRetryMin": "重试次数必须至少为 1 次", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP 方法", "selectHttpMethod": "选择 HTTP 方法", "domainPickerSubdomainLabel": "子域名", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "输入一个子域名以搜索并从可用免费域名中选择。", "domainPickerFreeDomains": "免费域名", "domainPickerSearchForAvailableDomains": "搜索可用域名", - "domainPickerNotWorkSelfHosted": "注意:自托管实例当前不提供免费的域名。", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "域名", "resourceEditDomain": "编辑域名", "siteName": "站点名称", @@ -1543,72 +1543,72 @@ "autoLoginError": "自动登录错误", "autoLoginErrorNoRedirectUrl": "未从身份提供商收到重定向URL。", "autoLoginErrorGeneratingUrl": "生成身份验证URL失败。", - "remoteExitNodeManageRemoteExitNodes": "管理自托管", - "remoteExitNodeDescription": "管理节点以扩展您的网络连接", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "搜索节点...", - "remoteExitNodeAdd": "添加节点", - "remoteExitNodeErrorDelete": "删除节点时出错", - "remoteExitNodeQuestionRemove": "您确定要从组织中删除 {selectedNode} 节点吗?", - "remoteExitNodeMessageRemove": "一旦删除,该节点将不再能够访问。", - "remoteExitNodeMessageConfirm": "要确认,请输入以下节点的名称。", - "remoteExitNodeConfirmDelete": "确认删除节点", - "remoteExitNodeDelete": "删除节点", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "创建节点", - "description": "创建一个新节点来扩展您的网络连接", - "viewAllButton": "查看所有节点", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "创建策略", - "description": "选择此选项以手动配置您的节点或生成新凭据。", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "采纳节点", - "description": "如果您已经拥有该节点的凭据,请选择此项。" + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "生成密钥", - "description": "如果您想为节点生成新密钥,请选择此选项" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "采纳现有节点", - "description": "输入您想要采用的现有节点的凭据", - "nodeIdLabel": "节点 ID", - "nodeIdDescription": "您想要采用的现有节点的 ID", - "secretLabel": "密钥", - "secretDescription": "现有节点的秘密密钥", - "submitButton": "采用节点" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "生成的凭据", - "description": "使用这些生成的凭据来配置您的节点", - "nodeIdTitle": "节点 ID", - "secretTitle": "密钥", - "saveCredentialsTitle": "将凭据添加到配置中", - "saveCredentialsDescription": "将这些凭据添加到您的自托管 Pangolin 节点配置文件中以完成连接。", - "submitButton": "创建节点" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "在通过现有节点时需要节点ID和密钥" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "无法加载默认值", - "defaultsNotLoaded": "默认值未加载", - "createFailed": "创建节点失败" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "节点创建成功" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "节点选择", - "remoteExitNodeSelectionDescription": "为此本地站点选择要路由流量的节点", - "remoteExitNodeRequired": "必须为本地站点选择节点", - "noRemoteExitNodesAvailable": "无可用节点", - "noRemoteExitNodesAvailableDescription": "此组织没有可用的节点。首先创建一个节点来使用本地站点。", - "exitNode": "出口节点", - "country": "国家", - "rulesMatchCountry": "当前基于源 IP", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "托管自托管", "description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "检测到国际域", "willbestoredas": "储存为:", - "roleMappingDescription": "确定当用户启用自动配送时如何分配他们的角色。", - "selectRole": "选择角色", - "roleMappingExpression": "表达式", - "selectRolePlaceholder": "选择角色", - "selectRoleDescription": "选择一个角色,从此身份提供商分配给所有用户", - "roleMappingExpressionDescription": "输入一个 JMESPath 表达式来从 ID 令牌提取角色信息", - "idpTenantIdRequired": "租户ID是必需的", - "invalidValue": "无效的值", - "idpTypeLabel": "身份提供者类型", - "roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'", - "idpGoogleConfiguration": "Google 配置", - "idpGoogleConfigurationDescription": "配置您的 Google OAuth2 凭据", - "idpGoogleClientIdDescription": "您的 Google OAuth2 客户端 ID", - "idpGoogleClientSecretDescription": "您的 Google OAuth2 客户端密钥", - "idpAzureConfiguration": "Azure Entra ID 配置", - "idpAzureConfigurationDescription": "配置您的 Azure Entra ID OAuth2 凭据", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "您的租户ID", - "idpAzureTenantIdDescription": "您的 Azure 租户ID (在 Azure Active Directory 概览中发现)", - "idpAzureClientIdDescription": "您的 Azure 应用程序注册客户端 ID", - "idpAzureClientSecretDescription": "您的 Azure 应用程序注册客户端密钥", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google 配置", - "idpAzureConfigurationTitle": "Azure Entra ID 配置", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "您的 Azure 应用程序注册客户端 ID", - "idpAzureClientSecretDescription2": "您的 Azure 应用程序注册客户端密钥", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC 提供商", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "子网", - "subnetDescription": "此组织网络配置的子网。", - "authPage": "认证页面", - "authPageDescription": "配置您的组织认证页面", - "authPageDomain": "认证页面域", - "noDomainSet": "没有域设置", - "changeDomain": "更改域", - "selectDomain": "选择域", - "restartCertificate": "重新启动证书", - "editAuthPageDomain": "编辑认证页面域", - "setAuthPageDomain": "设置认证页面域", - "failedToFetchCertificate": "获取证书失败", - "failedToRestartCertificate": "重新启动证书失败", - "addDomainToEnableCustomAuthPages": "为您的组织添加域名以启用自定义认证页面", - "selectDomainForOrgAuthPage": "选择组织认证页面的域", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "提供的域", "domainPickerFreeProvidedDomain": "免费提供的域", "domainPickerVerified": "已验证", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" 无法为 {domain} 变为有效。", "domainPickerSubdomainSanitized": "子域已净化", "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"", - "orgAuthSignInTitle": "登录到您的组织", - "orgAuthChooseIdpDescription": "选择您的身份提供商以继续", - "orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。", - "orgAuthSignInWithPangolin": "使用 Pangolin 登录", - "subscriptionRequiredToUse": "需要订阅才能使用此功能。", - "idpDisabled": "身份提供者已禁用。", - "orgAuthPageDisabled": "组织认证页面已禁用。", - "domainRestartedDescription": "域验证重新启动成功", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", - "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。" + "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From cabaa2e6d6ea47a4c49666065b9b3477a0f8837b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:26 -0700 Subject: [PATCH 140/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 351 ++++++++++++++++++++++---------------------- 1 file changed, 178 insertions(+), 173 deletions(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index 686a6f4d..8d789949 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Enkleste måte å opprette et inngangspunkt i nettverket ditt. Ingen ekstra oppsett.", "siteWg": "Grunnleggende WireGuard", "siteWgDescription": "Bruk hvilken som helst WireGuard-klient for å etablere en tunnel. Manuell NAT-oppsett kreves.", - "siteWgDescriptionSaas": "Bruk hvilken som helst WireGuard-klient for å etablere en tunnel. Manuell NAT-oppsett er nødvendig. FUNGERER KUN PÅ SELVHOSTEDE NODER", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Kun lokale ressurser. Ingen tunnelering.", - "siteLocalDescriptionSaas": "Kun lokale ressurser. Ingen tunneling. FUNGERER KUN PÅ SELVHOSTEDE NODER", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Se alle områder", "siteTunnelDescription": "Bestem hvordan du vil koble deg til ditt område", "siteNewtCredentials": "Newt påloggingsinformasjon", @@ -118,7 +118,7 @@ "usageExamples": "Brukseksempler", "tokenId": "Token-ID", "requestHeades": "Request Headers", - "queryParameter": "Forespørsel Params", + "queryParameter": "Query Parameter", "importantNote": "Viktig merknad", "shareImportantDescription": "Av sikkerhetsgrunner anbefales det å bruke headere fremfor query parametere der det er mulig, da query parametere kan logges i serverlogger eller nettleserhistorikk.", "token": "Token", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-ressurs", "resourceHTTPDescription": "Proxy-forespørsler til appen din over HTTPS ved bruk av et underdomene eller grunndomene.", "resourceRaw": "Rå TCP/UDP-ressurs", - "resourceRawDescription": "Proxyer forespørsler til appen din over TCP/UDP ved å bruke et portnummer.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Opprett ressurs", "resourceCreateDescription": "Følg trinnene nedenfor for å opprette en ny ressurs", "resourceSeeAll": "Se alle ressurser", @@ -168,9 +168,9 @@ "siteSelect": "Velg område", "siteSearch": "Søk i område", "siteNotFound": "Ingen område funnet.", - "selectCountry": "Velg land", - "searchCountries": "Søk land...", - "noCountryFound": "Ingen land funnet.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Dette området vil gi tilkobling til mål.", "resourceType": "Ressurstype", "resourceTypeDescription": "Bestem hvordan du vil få tilgang til ressursen din", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Underdomene: {subdomain}", "domainPickerNamespace": "Navnerom: {namespace}", "domainPickerShowMore": "Vis mer", - "regionSelectorTitle": "Velg Region", - "regionSelectorInfo": "Å velge en region hjelper oss med å gi bedre ytelse for din lokasjon. Du trenger ikke være i samme region som serveren.", - "regionSelectorPlaceholder": "Velg en region", - "regionSelectorComingSoon": "Kommer snart", - "billingLoadingSubscription": "Laster abonnement...", - "billingFreeTier": "Gratis nivå", - "billingWarningOverLimit": "Advarsel: Du har overskredet en eller flere bruksgrenser. Nettstedene dine vil ikke koble til før du endrer abonnementet ditt eller justerer bruken.", - "billingUsageLimitsOverview": "Oversikt over bruksgrenser", - "billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@fossorial.io.", - "billingDataUsage": "Databruk", - "billingOnlineTime": "Online tid for nettsteder", - "billingUsers": "Aktive brukere", - "billingDomains": "Aktive domener", - "billingRemoteExitNodes": "Aktive selvstyrte noder", - "billingNoLimitConfigured": "Ingen grense konfigurert", - "billingEstimatedPeriod": "Estimert faktureringsperiode", - "billingIncludedUsage": "Inkludert Bruk", - "billingIncludedUsageDescription": "Bruk inkludert i din nåværende abonnementsplan", - "billingFreeTierIncludedUsage": "Gratis nivå bruksgrenser", - "billingIncluded": "inkludert", - "billingEstimatedTotal": "Estimert Totalt:", - "billingNotes": "Notater", - "billingEstimateNote": "Dette er et estimat basert på din nåværende bruk.", - "billingActualChargesMayVary": "Faktiske kostnader kan variere.", - "billingBilledAtEnd": "Du vil bli fakturert ved slutten av faktureringsperioden.", - "billingModifySubscription": "Endre abonnement", - "billingStartSubscription": "Start abonnement", - "billingRecurringCharge": "Innkommende Avgift", - "billingManageSubscriptionSettings": "Administrer abonnementsinnstillinger og preferanser", - "billingNoActiveSubscription": "Du har ikke et aktivt abonnement. Start abonnementet ditt for å øke bruksgrensene.", - "billingFailedToLoadSubscription": "Klarte ikke å laste abonnement", - "billingFailedToLoadUsage": "Klarte ikke å laste bruksdata", - "billingFailedToGetCheckoutUrl": "Mislyktes å få betalingslenke", - "billingPleaseTryAgainLater": "Vennligst prøv igjen senere.", - "billingCheckoutError": "Kasserror", - "billingFailedToGetPortalUrl": "Mislyktes å hente portal URL", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", "billingPortalError": "Portal Error", - "billingDataUsageInfo": "Du er ladet for all data som overføres gjennom dine sikre tunneler når du er koblet til skyen. Dette inkluderer både innkommende og utgående trafikk på alle dine nettsteder. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Data belastes ikke ved bruk av EK-grupper.", - "billingOnlineTimeInfo": "Du er ladet på hvor lenge sidene dine forblir koblet til skyen. For eksempel tilsvarer 44,640 minutter ett nettsted som går 24/7 i en hel måned. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Tid belastes ikke når du bruker noder.", - "billingUsersInfo": "Du belastes for hver bruker i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive brukerkontoer i organisasjonen din.", - "billingDomainInfo": "Du belastes for hvert domene i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive domenekontoer i organisasjonen din.", - "billingRemoteExitNodesInfo": "Du belastes for hver styrt node i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive styrte noder i organisasjonen din.", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Domene ikke funnet", "domainNotFoundDescription": "Denne ressursen er deaktivert fordi domenet ikke lenger eksisterer i systemet vårt. Vennligst angi et nytt domene for denne ressursen.", "failed": "Mislyktes", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-endringer kan ta litt tid å propagere over internett. Dette kan ta fra noen få minutter til 48 timer, avhengig av din DNS-leverandør og TTL-innstillinger.", "resourcePortRequired": "Portnummer er påkrevd for ikke-HTTP-ressurser", "resourcePortNotAllowed": "Portnummer skal ikke angis for HTTP-ressurser", - "billingPricingCalculatorLink": "Pris Kalkulator", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Jeg godtar", "termsOfService": "brukervilkårene", @@ -1412,41 +1412,41 @@ "addNewTarget": "Legg til nytt mål", "targetsList": "Liste over mål", "targetErrorDuplicateTargetFound": "Duplikat av mål funnet", - "healthCheckHealthy": "Sunn", - "healthCheckUnhealthy": "Usunn", - "healthCheckUnknown": "Ukjent", - "healthCheck": "Helsekontroll", - "configureHealthCheck": "Konfigurer Helsekontroll", - "configureHealthCheckDescription": "Sett opp helsekontroll for {target}", - "enableHealthChecks": "Aktiver Helsekontroller", - "enableHealthChecksDescription": "Overvåk helsen til dette målet. Du kan overvåke et annet endepunkt enn målet hvis nødvendig.", - "healthScheme": "Metode", - "healthSelectScheme": "Velg metode", - "healthCheckPath": "Sti", - "healthHostname": "IP / Vert", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", + "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "Stien for å sjekke helsestatus.", - "healthyIntervalSeconds": "Sunt intervall", - "unhealthyIntervalSeconds": "Usunt intervall", - "IntervalSeconds": "Sunt intervall", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Tid er i sekunder", - "retryAttempts": "Forsøk på nytt", - "expectedResponseCodes": "Forventede svarkoder", - "expectedResponseCodesDescription": "HTTP-statuskode som indikerer sunn status. Hvis den blir stående tom, regnes 200-300 som sunn.", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Egendefinerte topptekster", - "customHeadersDescription": "Overskrifter som er adskilt med linje: Overskriftsnavn: verdi", - "headersValidationError": "Topptekst må være i formatet: header-navn: verdi.", - "saveHealthCheck": "Lagre Helsekontroll", - "healthCheckSaved": "Helsekontroll Lagret", - "healthCheckSavedDescription": "Helsekontrollkonfigurasjonen ble lagret", - "healthCheckError": "Helsekontrollfeil", - "healthCheckErrorDescription": "Det oppstod en feil under lagring av helsekontrollkonfigurasjonen", - "healthCheckPathRequired": "Helsekontrollsti er påkrevd", - "healthCheckMethodRequired": "HTTP-metode er påkrevd", - "healthCheckIntervalMin": "Sjekkeintervallet må være minst 5 sekunder", - "healthCheckTimeoutMin": "Timeout må være minst 1 sekund", - "healthCheckRetryMin": "Forsøk på nytt må være minst 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "HTTP-metode", "selectHttpMethod": "Velg HTTP-metode", "domainPickerSubdomainLabel": "Underdomene", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Skriv inn et underdomene for å søke og velge blant tilgjengelige gratis domener.", "domainPickerFreeDomains": "Gratis domener", "domainPickerSearchForAvailableDomains": "Søk etter tilgjengelige domener", - "domainPickerNotWorkSelfHosted": "Merk: Gratis tilbudte domener er ikke tilgjengelig for selv-hostede instanser akkurat nå.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Domene", "resourceEditDomain": "Rediger domene", "siteName": "Områdenavn", @@ -1543,72 +1543,72 @@ "autoLoginError": "Feil ved automatisk innlogging", "autoLoginErrorNoRedirectUrl": "Ingen omdirigerings-URL mottatt fra identitetsleverandøren.", "autoLoginErrorGeneratingUrl": "Kunne ikke generere autentiserings-URL.", - "remoteExitNodeManageRemoteExitNodes": "Administrer Selv-Hostet", - "remoteExitNodeDescription": "Administrer noder for å forlenge nettverkstilkoblingen din", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Søk noder...", - "remoteExitNodeAdd": "Legg til Node", - "remoteExitNodeErrorDelete": "Feil ved sletting av node", - "remoteExitNodeQuestionRemove": "Er du sikker på at du vil fjerne noden {selectedNode} fra organisasjonen?", - "remoteExitNodeMessageRemove": "Når noden er fjernet, vil ikke lenger være tilgjengelig.", - "remoteExitNodeMessageConfirm": "For å bekrefte, skriv inn navnet på noden nedenfor.", - "remoteExitNodeConfirmDelete": "Bekreft sletting av Node", - "remoteExitNodeDelete": "Slett Node", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Opprett node", - "description": "Opprett en ny node for å utvide nettverkstilkoblingen din", - "viewAllButton": "Vis alle koder", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Opprettelsesstrategi", - "description": "Velg denne for manuelt å konfigurere noden eller generere nye legitimasjoner.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adopter Node", - "description": "Velg dette hvis du allerede har legitimasjon til noden." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Generer Nøkler", - "description": "Velg denne hvis du vil generere nye nøkler for noden" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adopter Eksisterende Node", - "description": "Skriv inn opplysningene til den eksisterende noden du vil adoptere", + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", "nodeIdLabel": "Node ID", - "nodeIdDescription": "ID-en til den eksisterende noden du vil adoptere", - "secretLabel": "Sikkerhetsnøkkel", - "secretDescription": "Den hemmelige nøkkelen til en eksisterende node", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", "submitButton": "Adopt Node" }, "generate": { - "title": "Genererte Legitimasjoner", - "description": "Bruk disse genererte opplysningene for å konfigurere noden din", + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", "nodeIdTitle": "Node ID", - "secretTitle": "Sikkerhet", - "saveCredentialsTitle": "Legg til Legitimasjoner til Config", - "saveCredentialsDescription": "Legg til disse legitimasjonene i din selv-hostede Pangolin node-konfigurasjonsfil for å fullføre koblingen.", - "submitButton": "Opprett node" + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "Node ID og Secret er påkrevd når du adopterer en eksisterende node" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Feil ved lasting av standarder", - "defaultsNotLoaded": "Standarder ikke lastet", - "createFailed": "Kan ikke opprette node" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Node opprettet" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Noden utvalg", - "remoteExitNodeSelectionDescription": "Velg en node for å sende trafikk gjennom for dette lokale nettstedet", - "remoteExitNodeRequired": "En node må velges for lokale nettsteder", - "noRemoteExitNodesAvailable": "Ingen noder tilgjengelig", - "noRemoteExitNodesAvailableDescription": "Ingen noder er tilgjengelige for denne organisasjonen. Opprett en node først for å bruke lokale nettsteder.", - "exitNode": "Utgangsnode", - "country": "Land", - "rulesMatchCountry": "For tiden basert på kilde IP", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Administrert selv-hostet", "description": "Sikre og lavvedlikeholdsservere, selvbetjente Pangolin med ekstra klokker, og understell", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internasjonalt domene oppdaget", "willbestoredas": "Vil bli lagret som:", - "roleMappingDescription": "Bestem hvordan roller tilordnes brukere når innloggingen er aktivert når autog-rapportering er aktivert.", - "selectRole": "Velg en rolle", - "roleMappingExpression": "Uttrykk", - "selectRolePlaceholder": "Velg en rolle", - "selectRoleDescription": "Velg en rolle å tilordne alle brukere fra denne identitet leverandøren", - "roleMappingExpressionDescription": "Skriv inn et JMESPath uttrykk for å hente rolleinformasjon fra ID-nøkkelen", - "idpTenantIdRequired": "Bedriftens ID kreves", - "invalidValue": "Ugyldig verdi", - "idpTypeLabel": "Identitet leverandør type", - "roleMappingExpressionPlaceholder": "F.eks. inneholder(grupper, 'admin') && 'Admin' ⋅'Medlem'", - "idpGoogleConfiguration": "Google Konfigurasjon", - "idpGoogleConfigurationDescription": "Konfigurer din Google OAuth2 legitimasjon", - "idpGoogleClientIdDescription": "Din Google OAuth2-klient-ID", - "idpGoogleClientSecretDescription": "Google OAuth2-klienten din hemmelig", - "idpAzureConfiguration": "Azure Entra ID konfigurasjon", - "idpAzureConfigurationDescription": "Konfigurere din Azure Entra ID OAuth2 legitimasjon", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "din-tenant-id", - "idpAzureTenantIdDescription": "Din Azure leie-ID (funnet i Azure Active Directory-oversikten)", - "idpAzureClientIdDescription": "Din Azure App registrerings klient-ID", - "idpAzureClientSecretDescription": "Din Azure App registrerings klient hemmelig", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Konfigurasjon", - "idpAzureConfigurationTitle": "Azure Entra ID konfigurasjon", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Din Azure App registrerings klient-ID", - "idpAzureClientSecretDescription2": "Din Azure App registrerings klient hemmelig", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC leverandør", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnett", - "subnetDescription": "Undernettverket for denne organisasjonens nettverkskonfigurasjon.", - "authPage": "Autentiseringsside", - "authPageDescription": "Konfigurer autoriseringssiden for din organisasjon", - "authPageDomain": "Autentiseringsside domene", - "noDomainSet": "Ingen domene valgt", - "changeDomain": "Endre domene", - "selectDomain": "Velg domene", - "restartCertificate": "Omstart sertifikat", - "editAuthPageDomain": "Rediger auth sidedomene", - "setAuthPageDomain": "Angi autoriseringsside domene", - "failedToFetchCertificate": "Kunne ikke hente sertifikat", - "failedToRestartCertificate": "Kan ikke starte sertifikat", - "addDomainToEnableCustomAuthPages": "Legg til et domene for å aktivere egendefinerte autentiseringssider for organisasjonen din", - "selectDomainForOrgAuthPage": "Velg et domene for organisasjonens autentiseringsside", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", + "authPageDomain": "Auth Page Domain", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Gitt domene", "domainPickerFreeProvidedDomain": "Gratis oppgitt domene", "domainPickerVerified": "Bekreftet", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kunne ikke gjøres gyldig for {domain}.", "domainPickerSubdomainSanitized": "Underdomenet som ble sanivert", "domainPickerSubdomainCorrected": "\"{sub}\" var korrigert til \"{sanitized}\"", - "orgAuthSignInTitle": "Logg inn på din organisasjon", - "orgAuthChooseIdpDescription": "Velg din identitet leverandør for å fortsette", - "orgAuthNoIdpConfigured": "Denne organisasjonen har ikke noen identitetstjeneste konfigurert. Du kan i stedet logge inn med Pangolin identiteten din.", - "orgAuthSignInWithPangolin": "Logg inn med Pangolin", - "subscriptionRequiredToUse": "Et abonnement er påkrevd for å bruke denne funksjonen.", - "idpDisabled": "Identitetsleverandører er deaktivert.", - "orgAuthPageDisabled": "Informasjons-siden for organisasjon er deaktivert.", - "domainRestartedDescription": "Domene-verifiseringen ble startet på nytt", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", - "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her." + "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From f90e6bef9e92e7bd764b79586544f7b7979afd62 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 21:10:27 -0700 Subject: [PATCH 141/322] New translations en-us.json (Spanish) --- messages/es-ES.json | 363 ++++++++++++++++++++++---------------------- 1 file changed, 184 insertions(+), 179 deletions(-) diff --git a/messages/es-ES.json b/messages/es-ES.json index d48e77b5..c816ca6c 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -67,7 +67,7 @@ "siteDocker": "Expandir para detalles de despliegue de Docker", "toggle": "Cambiar", "dockerCompose": "Componer Docker", - "dockerRun": "Ejecutar Docker", + "dockerRun": "Docker Run", "siteLearnLocal": "Los sitios locales no tienen túnel, aprender más", "siteConfirmCopy": "He copiado la configuración", "searchSitesProgress": "Buscar sitios...", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en tu red. Sin configuración adicional.", "siteWg": "Wirex Guardia Básica", "siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.", - "siteWgDescriptionSaas": "Utilice cualquier cliente de WireGuard para establecer un túnel. Se requiere configuración manual de NAT. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS", + "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", "siteLocalDescription": "Solo recursos locales. Sin túneles.", - "siteLocalDescriptionSaas": "Solo recursos locales. Sin túneles. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS", + "siteLocalDescriptionSaas": "Local resources only. No tunneling.", "siteSeeAll": "Ver todos los sitios", "siteTunnelDescription": "Determina cómo quieres conectarte a tu sitio", "siteNewtCredentials": "Credenciales nuevas", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS Recurso", "resourceHTTPDescription": "Solicitudes de proxy a tu aplicación sobre HTTPS usando un subdominio o dominio base.", "resourceRaw": "Recurso TCP/UDP sin procesar", - "resourceRawDescription": "Solicitudes de proxy a tu aplicación a través de TCP/UDP usando un número de puerto.", + "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", "resourceCreate": "Crear Recurso", "resourceCreateDescription": "Siga los siguientes pasos para crear un nuevo recurso", "resourceSeeAll": "Ver todos los recursos", @@ -168,9 +168,9 @@ "siteSelect": "Seleccionar sitio", "siteSearch": "Buscar sitio", "siteNotFound": "Sitio no encontrado.", - "selectCountry": "Seleccionar país", - "searchCountries": "Buscar países...", - "noCountryFound": "Ningún país encontrado.", + "selectCountry": "Select country", + "searchCountries": "Search countries...", + "noCountryFound": "No country found.", "siteSelectionDescription": "Este sitio proporcionará conectividad al objetivo.", "resourceType": "Tipo de recurso", "resourceTypeDescription": "Determina cómo quieres acceder a tu recurso", @@ -817,7 +817,7 @@ "redirectUrl": "URL de redirección", "redirectUrlAbout": "Acerca de la URL de redirección", "redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.", - "pangolinAuth": "Autenticación - Pangolin", + "pangolinAuth": "Auth - Pangolin", "verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.", "errorOccurred": "Se ha producido un error", "emailErrorVerify": "No se pudo verificar el email:", @@ -1220,7 +1220,7 @@ "billing": "Facturación", "orgBillingDescription": "Gestiona tu información de facturación y suscripciones", "github": "GitHub", - "pangolinHosted": "Pangolin Alojado", + "pangolinHosted": "Pangolin Hosted", "fossorial": "Fossorial", "completeAccountSetup": "Completar configuración de cuenta", "completeAccountSetupDescription": "Establece tu contraseña para comenzar", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdominio: {subdomain}", "domainPickerNamespace": "Espacio de nombres: {namespace}", "domainPickerShowMore": "Mostrar más", - "regionSelectorTitle": "Seleccionar Región", - "regionSelectorInfo": "Seleccionar una región nos ayuda a brindar un mejor rendimiento para tu ubicación. No tienes que estar en la misma región que tu servidor.", - "regionSelectorPlaceholder": "Elige una región", - "regionSelectorComingSoon": "Próximamente", - "billingLoadingSubscription": "Cargando suscripción...", - "billingFreeTier": "Nivel Gratis", - "billingWarningOverLimit": "Advertencia: Has excedido uno o más límites de uso. Tus sitios no se conectarán hasta que modifiques tu suscripción o ajustes tu uso.", - "billingUsageLimitsOverview": "Descripción general de los límites de uso", - "billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@fossorial.io.", - "billingDataUsage": "Uso de datos", - "billingOnlineTime": "Tiempo en línea del sitio", - "billingUsers": "Usuarios activos", - "billingDomains": "Dominios activos", - "billingRemoteExitNodes": "Nodos autogestionados activos", - "billingNoLimitConfigured": "No se ha configurado ningún límite", - "billingEstimatedPeriod": "Período de facturación estimado", - "billingIncludedUsage": "Uso incluido", - "billingIncludedUsageDescription": "Uso incluido con su plan de suscripción actual", - "billingFreeTierIncludedUsage": "Permisos de uso del nivel gratuito", - "billingIncluded": "incluido", - "billingEstimatedTotal": "Total Estimado:", - "billingNotes": "Notas", - "billingEstimateNote": "Esta es una estimación basada en tu uso actual.", - "billingActualChargesMayVary": "Los cargos reales pueden variar.", - "billingBilledAtEnd": "Se te facturará al final del período de facturación.", - "billingModifySubscription": "Modificar Suscripción", - "billingStartSubscription": "Iniciar Suscripción", - "billingRecurringCharge": "Cargo Recurrente", - "billingManageSubscriptionSettings": "Administra la configuración y preferencias de tu suscripción", - "billingNoActiveSubscription": "No tienes una suscripción activa. Inicia tu suscripción para aumentar los límites de uso.", - "billingFailedToLoadSubscription": "Error al cargar la suscripción", - "billingFailedToLoadUsage": "Error al cargar el uso", - "billingFailedToGetCheckoutUrl": "Error al obtener la URL de pago", - "billingPleaseTryAgainLater": "Por favor, inténtelo de nuevo más tarde.", - "billingCheckoutError": "Error de pago", - "billingFailedToGetPortalUrl": "Error al obtener la URL del portal", - "billingPortalError": "Error del portal", - "billingDataUsageInfo": "Se le cobran todos los datos transferidos a través de sus túneles seguros cuando se conectan a la nube. Esto incluye tanto tráfico entrante como saliente a través de todos sus sitios. Cuando alcance su límite, sus sitios se desconectarán hasta que actualice su plan o reduzca el uso. Los datos no se cargan cuando se usan nodos.", - "billingOnlineTimeInfo": "Se te cobrará en función del tiempo que tus sitios permanezcan conectados a la nube. Por ejemplo, 44.640 minutos equivale a un sitio que funciona 24/7 durante un mes completo. Cuando alcance su límite, sus sitios se desconectarán hasta que mejore su plan o reduzca el uso. No se cargará el tiempo al usar nodos.", - "billingUsersInfo": "Se te cobra por cada usuario en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de usuario activas en tu organización.", - "billingDomainInfo": "Se te cobra por cada dominio en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de dominio activas en tu organización.", - "billingRemoteExitNodesInfo": "Se te cobra por cada nodo gestionado en tu organización. La facturación se calcula diariamente según la cantidad de nodos gestionados activos en tu organización.", + "regionSelectorTitle": "Select Region", + "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", + "regionSelectorPlaceholder": "Choose a region", + "regionSelectorComingSoon": "Coming Soon", + "billingLoadingSubscription": "Loading subscription...", + "billingFreeTier": "Free Tier", + "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", + "billingUsageLimitsOverview": "Usage Limits Overview", + "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", + "billingDataUsage": "Data Usage", + "billingOnlineTime": "Site Online Time", + "billingUsers": "Active Users", + "billingDomains": "Active Domains", + "billingRemoteExitNodes": "Active Self-hosted Nodes", + "billingNoLimitConfigured": "No limit configured", + "billingEstimatedPeriod": "Estimated Billing Period", + "billingIncludedUsage": "Included Usage", + "billingIncludedUsageDescription": "Usage included with your current subscription plan", + "billingFreeTierIncludedUsage": "Free tier usage allowances", + "billingIncluded": "included", + "billingEstimatedTotal": "Estimated Total:", + "billingNotes": "Notes", + "billingEstimateNote": "This is an estimate based on your current usage.", + "billingActualChargesMayVary": "Actual charges may vary.", + "billingBilledAtEnd": "You will be billed at the end of the billing period.", + "billingModifySubscription": "Modify Subscription", + "billingStartSubscription": "Start Subscription", + "billingRecurringCharge": "Recurring Charge", + "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", + "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", + "billingFailedToLoadSubscription": "Failed to load subscription", + "billingFailedToLoadUsage": "Failed to load usage", + "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", + "billingPleaseTryAgainLater": "Please try again later.", + "billingCheckoutError": "Checkout Error", + "billingFailedToGetPortalUrl": "Failed to get portal URL", + "billingPortalError": "Portal Error", + "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", + "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", + "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", + "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", + "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", "domainNotFound": "Dominio no encontrado", "domainNotFoundDescription": "Este recurso está deshabilitado porque el dominio ya no existe en nuestro sistema. Por favor, establece un nuevo dominio para este recurso.", "failed": "Fallido", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Los cambios de DNS pueden tardar un tiempo en propagarse a través de internet. Esto puede tardar desde unos pocos minutos hasta 48 horas, dependiendo de tu proveedor de DNS y la configuración de TTL.", "resourcePortRequired": "Se requiere número de puerto para recursos no HTTP", "resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP", - "billingPricingCalculatorLink": "Calculadora de Precios", + "billingPricingCalculatorLink": "Pricing Calculator", "signUpTerms": { "IAgreeToThe": "Estoy de acuerdo con los", "termsOfService": "términos del servicio", @@ -1412,41 +1412,41 @@ "addNewTarget": "Agregar nuevo destino", "targetsList": "Lista de destinos", "targetErrorDuplicateTargetFound": "Se encontró un destino duplicado", - "healthCheckHealthy": "Saludable", - "healthCheckUnhealthy": "No saludable", - "healthCheckUnknown": "Desconocido", - "healthCheck": "Chequeo de salud", - "configureHealthCheck": "Configurar Chequeo de Salud", - "configureHealthCheckDescription": "Configura la monitorización de salud para {target}", - "enableHealthChecks": "Activar Chequeos de Salud", - "enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.", - "healthScheme": "Método", - "healthSelectScheme": "Seleccionar método", - "healthCheckPath": "Ruta", + "healthCheckHealthy": "Healthy", + "healthCheckUnhealthy": "Unhealthy", + "healthCheckUnknown": "Unknown", + "healthCheck": "Health Check", + "configureHealthCheck": "Configure Health Check", + "configureHealthCheckDescription": "Set up health monitoring for {target}", + "enableHealthChecks": "Enable Health Checks", + "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", + "healthScheme": "Method", + "healthSelectScheme": "Select Method", + "healthCheckPath": "Path", "healthHostname": "IP / Host", - "healthPort": "Puerto", - "healthCheckPathDescription": "La ruta para comprobar el estado de salud.", - "healthyIntervalSeconds": "Intervalo Saludable", - "unhealthyIntervalSeconds": "Intervalo No Saludable", - "IntervalSeconds": "Intervalo Saludable", - "timeoutSeconds": "Tiempo de Espera", - "timeIsInSeconds": "El tiempo está en segundos", - "retryAttempts": "Intentos de Reintento", - "expectedResponseCodes": "Códigos de respuesta esperados", - "expectedResponseCodesDescription": "Código de estado HTTP que indica un estado saludable. Si se deja en blanco, se considera saludable de 200 a 300.", + "healthPort": "Port", + "healthCheckPathDescription": "The path to check for health status.", + "healthyIntervalSeconds": "Healthy Interval", + "unhealthyIntervalSeconds": "Unhealthy Interval", + "IntervalSeconds": "Healthy Interval", + "timeoutSeconds": "Timeout", + "timeIsInSeconds": "Time is in seconds", + "retryAttempts": "Retry Attempts", + "expectedResponseCodes": "Expected Response Codes", + "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", "customHeaders": "Cabeceras personalizadas", - "customHeadersDescription": "Nueva línea de cabeceras separada: Nombre de cabecera: valor", - "headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.", - "saveHealthCheck": "Guardar Chequeo de Salud", - "healthCheckSaved": "Chequeo de Salud Guardado", - "healthCheckSavedDescription": "La configuración del chequeo de salud se ha guardado correctamente", - "healthCheckError": "Error en el Chequeo de Salud", - "healthCheckErrorDescription": "Ocurrió un error al guardar la configuración del chequeo de salud", - "healthCheckPathRequired": "Se requiere la ruta del chequeo de salud", - "healthCheckMethodRequired": "Se requiere el método HTTP", - "healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos", - "healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo", - "healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1", + "customHeadersDescription": "Headers new line separated: Header-Name: value", + "headersValidationError": "Headers must be in the format: Header-Name: value", + "saveHealthCheck": "Save Health Check", + "healthCheckSaved": "Health Check Saved", + "healthCheckSavedDescription": "Health check configuration has been saved successfully", + "healthCheckError": "Health Check Error", + "healthCheckErrorDescription": "An error occurred while saving the health check configuration", + "healthCheckPathRequired": "Health check path is required", + "healthCheckMethodRequired": "HTTP method is required", + "healthCheckIntervalMin": "Check interval must be at least 5 seconds", + "healthCheckTimeoutMin": "Timeout must be at least 1 second", + "healthCheckRetryMin": "Retry attempts must be at least 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Seleccionar método HTTP", "domainPickerSubdomainLabel": "Subdominio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Ingrese un subdominio para buscar y seleccionar entre dominios gratuitos disponibles.", "domainPickerFreeDomains": "Dominios gratuitos", "domainPickerSearchForAvailableDomains": "Buscar dominios disponibles", - "domainPickerNotWorkSelfHosted": "Nota: Los dominios gratuitos proporcionados no están disponibles para instancias autogestionadas por ahora.", + "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", "resourceDomain": "Dominio", "resourceEditDomain": "Editar dominio", "siteName": "Nombre del sitio", @@ -1543,72 +1543,72 @@ "autoLoginError": "Error de inicio de sesión automático", "autoLoginErrorNoRedirectUrl": "No se recibió URL de redirección del proveedor de identidad.", "autoLoginErrorGeneratingUrl": "Error al generar URL de autenticación.", - "remoteExitNodeManageRemoteExitNodes": "Administrar Nodos Autogestionados", - "remoteExitNodeDescription": "Administrar nodos para extender la conectividad de red", + "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", + "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Buscar nodos...", - "remoteExitNodeAdd": "Añadir Nodo", - "remoteExitNodeErrorDelete": "Error al eliminar el nodo", - "remoteExitNodeQuestionRemove": "¿Está seguro de que desea eliminar el nodo {selectedNode} de la organización?", - "remoteExitNodeMessageRemove": "Una vez eliminado, el nodo ya no será accesible.", - "remoteExitNodeMessageConfirm": "Para confirmar, por favor escriba el nombre del nodo a continuación.", - "remoteExitNodeConfirmDelete": "Confirmar eliminar nodo", - "remoteExitNodeDelete": "Eliminar Nodo", + "searchRemoteExitNodes": "Search nodes...", + "remoteExitNodeAdd": "Add Node", + "remoteExitNodeErrorDelete": "Error deleting node", + "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", + "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", + "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", + "remoteExitNodeConfirmDelete": "Confirm Delete Node", + "remoteExitNodeDelete": "Delete Node", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Crear Nodo", - "description": "Crear un nuevo nodo para extender la conectividad de red", - "viewAllButton": "Ver todos los nodos", + "title": "Create Node", + "description": "Create a new node to extend your network connectivity", + "viewAllButton": "View All Nodes", "strategy": { - "title": "Estrategia de Creación", - "description": "Elija esto para configurar manualmente su nodo o generar nuevas credenciales.", + "title": "Creation Strategy", + "description": "Choose this to manually configure your node or generate new credentials.", "adopt": { - "title": "Adoptar Nodo", - "description": "Elija esto si ya tiene las credenciales para el nodo." + "title": "Adopt Node", + "description": "Choose this if you already have the credentials for the node." }, "generate": { - "title": "Generar Claves", - "description": "Elija esto si desea generar nuevas claves para el nodo" + "title": "Generate Keys", + "description": "Choose this if you want to generate new keys for the node" } }, "adopt": { - "title": "Adoptar Nodo Existente", - "description": "Introduzca las credenciales del nodo existente que desea adoptar", - "nodeIdLabel": "ID del nodo", - "nodeIdDescription": "El ID del nodo existente que desea adoptar", - "secretLabel": "Secreto", - "secretDescription": "La clave secreta del nodo existente", - "submitButton": "Adoptar Nodo" + "title": "Adopt Existing Node", + "description": "Enter the credentials of the existing node you want to adopt", + "nodeIdLabel": "Node ID", + "nodeIdDescription": "The ID of the existing node you want to adopt", + "secretLabel": "Secret", + "secretDescription": "The secret key of the existing node", + "submitButton": "Adopt Node" }, "generate": { - "title": "Credenciales Generadas", - "description": "Utilice estas credenciales generadas para configurar su nodo", - "nodeIdTitle": "ID del nodo", - "secretTitle": "Secreto", - "saveCredentialsTitle": "Agregar Credenciales a la Configuración", - "saveCredentialsDescription": "Agrega estas credenciales a tu archivo de configuración del nodo Pangolin autogestionado para completar la conexión.", - "submitButton": "Crear Nodo" + "title": "Generated Credentials", + "description": "Use these generated credentials to configure your node", + "nodeIdTitle": "Node ID", + "secretTitle": "Secret", + "saveCredentialsTitle": "Add Credentials to Config", + "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", + "submitButton": "Create Node" }, "validation": { - "adoptRequired": "El ID del nodo y el secreto son necesarios al adoptar un nodo existente" + "adoptRequired": "Node ID and Secret are required when adopting an existing node" }, "errors": { - "loadDefaultsFailed": "Falló al cargar los valores predeterminados", - "defaultsNotLoaded": "Valores predeterminados no cargados", - "createFailed": "Error al crear el nodo" + "loadDefaultsFailed": "Failed to load defaults", + "defaultsNotLoaded": "Defaults not loaded", + "createFailed": "Failed to create node" }, "success": { - "created": "Nodo creado correctamente" + "created": "Node created successfully" } }, - "remoteExitNodeSelection": "Selección de nodo", - "remoteExitNodeSelectionDescription": "Seleccione un nodo a través del cual enrutar el tráfico para este sitio local", - "remoteExitNodeRequired": "Un nodo debe ser seleccionado para sitios locales", - "noRemoteExitNodesAvailable": "No hay nodos disponibles", - "noRemoteExitNodesAvailableDescription": "No hay nodos disponibles para esta organización. Crea un nodo primero para usar sitios locales.", - "exitNode": "Nodo de Salida", - "country": "País", - "rulesMatchCountry": "Actualmente basado en IP de origen", + "remoteExitNodeSelection": "Node Selection", + "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", + "remoteExitNodeRequired": "A node must be selected for local sites", + "noRemoteExitNodesAvailable": "No Nodes Available", + "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "exitNode": "Exit Node", + "country": "Country", + "rulesMatchCountry": "Currently based on source IP", "managedSelfHosted": { "title": "Autogestionado", "description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internacional detectado", "willbestoredas": "Se almacenará como:", - "roleMappingDescription": "Determinar cómo se asignan los roles a los usuarios cuando se registran cuando está habilitada la provisión automática.", - "selectRole": "Seleccione un rol", - "roleMappingExpression": "Expresión", - "selectRolePlaceholder": "Elija un rol", - "selectRoleDescription": "Seleccione un rol para asignar a todos los usuarios de este proveedor de identidad", - "roleMappingExpressionDescription": "Introduzca una expresión JMESPath para extraer información de rol del token de ID", - "idpTenantIdRequired": "El ID del cliente es obligatorio", - "invalidValue": "Valor inválido", - "idpTypeLabel": "Tipo de proveedor de identidad", - "roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'", - "idpGoogleConfiguration": "Configuración de Google", - "idpGoogleConfigurationDescription": "Configura tus credenciales de Google OAuth2", - "idpGoogleClientIdDescription": "Tu ID de cliente de Google OAuth2", - "idpGoogleClientSecretDescription": "Tu secreto de cliente de Google OAuth2", - "idpAzureConfiguration": "Configuración de Azure Entra ID", - "idpAzureConfigurationDescription": "Configure sus credenciales de Azure Entra ID OAuth2", + "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", + "selectRole": "Select a Role", + "roleMappingExpression": "Expression", + "selectRolePlaceholder": "Choose a role", + "selectRoleDescription": "Select a role to assign to all users from this identity provider", + "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", + "idpTenantIdRequired": "Tenant ID is required", + "invalidValue": "Invalid value", + "idpTypeLabel": "Identity Provider Type", + "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google Configuration", + "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", + "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", + "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Configuration", + "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "su-inquilino-id", - "idpAzureTenantIdDescription": "Su ID de inquilino de Azure (encontrado en el resumen de Azure Active Directory)", - "idpAzureClientIdDescription": "Tu ID de Cliente de Registro de Azure App", - "idpAzureClientSecretDescription": "Tu Azure App Registro Cliente secreto", + "idpTenantIdPlaceholder": "your-tenant-id", + "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", + "idpAzureClientIdDescription": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Configuración de Google", - "idpAzureConfigurationTitle": "Configuración de Azure Entra ID", + "idpGoogleConfigurationTitle": "Google Configuration", + "idpAzureConfigurationTitle": "Azure Entra ID Configuration", "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Tu ID de Cliente de Registro de Azure App", - "idpAzureClientSecretDescription2": "Tu Azure App Registro Cliente secreto", + "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", "idpGoogleDescription": "Proveedor OAuth2/OIDC de Google", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subred", - "subnetDescription": "La subred para la configuración de red de esta organización.", - "authPage": "Página Auth", - "authPageDescription": "Configurar la página de autenticación de su organización", + "subnet": "Subnet", + "subnetDescription": "The subnet for this organization's network configuration.", + "authPage": "Auth Page", + "authPageDescription": "Configure the auth page for your organization", "authPageDomain": "Auth Page Domain", - "noDomainSet": "Ningún dominio establecido", - "changeDomain": "Cambiar dominio", - "selectDomain": "Seleccionar dominio", - "restartCertificate": "Reiniciar certificado", - "editAuthPageDomain": "Editar dominio Auth Page", - "setAuthPageDomain": "Establecer dominio Auth Page", - "failedToFetchCertificate": "Error al obtener el certificado", - "failedToRestartCertificate": "Error al reiniciar el certificado", - "addDomainToEnableCustomAuthPages": "Añadir un dominio para habilitar páginas de autenticación personalizadas para su organización", - "selectDomainForOrgAuthPage": "Seleccione un dominio para la página de autenticación de la organización", + "noDomainSet": "No domain set", + "changeDomain": "Change Domain", + "selectDomain": "Select Domain", + "restartCertificate": "Restart Certificate", + "editAuthPageDomain": "Edit Auth Page Domain", + "setAuthPageDomain": "Set Auth Page Domain", + "failedToFetchCertificate": "Failed to fetch certificate", + "failedToRestartCertificate": "Failed to restart certificate", + "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", + "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", "domainPickerProvidedDomain": "Dominio proporcionado", "domainPickerFreeProvidedDomain": "Dominio proporcionado gratis", "domainPickerVerified": "Verificado", @@ -1707,16 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "No se ha podido hacer válido \"{sub}\" para {domain}.", "domainPickerSubdomainSanitized": "Subdominio saneado", "domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"", - "orgAuthSignInTitle": "Inicia sesión en tu organización", - "orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar", - "orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.", - "orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin", - "subscriptionRequiredToUse": "Se requiere una suscripción para utilizar esta función.", - "idpDisabled": "Los proveedores de identidad están deshabilitados.", - "orgAuthPageDisabled": "La página de autenticación de la organización está deshabilitada.", - "domainRestartedDescription": "Verificación de dominio reiniciada con éxito", + "orgAuthSignInTitle": "Sign in to your organization", + "orgAuthChooseIdpDescription": "Choose your identity provider to continue", + "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", + "orgAuthSignInWithPangolin": "Sign in with Pangolin", + "subscriptionRequiredToUse": "A subscription is required to use this feature.", + "idpDisabled": "Identity providers are disabled.", + "orgAuthPageDisabled": "Organization auth page is disabled.", + "domainRestartedDescription": "Domain verification restarted successfully", "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", "emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.", - "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí." + "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí.", + "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", + "authPageUpdated": "Auth page updated successfully", + "healthCheckNotAvailable": "Local", + "rewritePath": "Rewrite Path", + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." } From 759661420eb0c9243d4f11c70558971a5cab2201 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:53 -0700 Subject: [PATCH 142/322] New translations en-us.json (French) --- messages/fr-FR.json | 362 ++++++++++++++++++++++---------------------- 1 file changed, 181 insertions(+), 181 deletions(-) diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 95a90c0c..6028ab5b 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.", "siteWg": "WireGuard basique", "siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES", "siteLocalDescription": "Ressources locales seulement. Pas de tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES", "siteSeeAll": "Voir tous les sites", "siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site", "siteNewtCredentials": "Identifiants Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Ressource HTTPS", "resourceHTTPDescription": "Requêtes de proxy à votre application via HTTPS en utilisant un sous-domaine ou un domaine de base.", "resourceRaw": "Ressource TCP/UDP brute", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Demandes de proxy à votre application via TCP/UDP en utilisant un numéro de port.", "resourceCreate": "Créer une ressource", "resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource", "resourceSeeAll": "Voir toutes les ressources", @@ -168,9 +168,9 @@ "siteSelect": "Sélectionner un site", "siteSearch": "Chercher un site", "siteNotFound": "Aucun site trouvé.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Sélectionnez un pays", + "searchCountries": "Recherchez des pays...", + "noCountryFound": "Aucun pays trouvé.", "siteSelectionDescription": "Ce site fournira la connectivité à la cible.", "resourceType": "Type de ressource", "resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Sous-domaine : {subdomain}", "domainPickerNamespace": "Espace de noms : {namespace}", "domainPickerShowMore": "Afficher plus", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", + "regionSelectorTitle": "Sélectionner Région", + "regionSelectorInfo": "Sélectionner une région nous aide à offrir de meilleures performances pour votre localisation. Vous n'avez pas besoin d'être dans la même région que votre serveur.", + "regionSelectorPlaceholder": "Choisissez une région", + "regionSelectorComingSoon": "Bientôt disponible", + "billingLoadingSubscription": "Chargement de l'abonnement...", + "billingFreeTier": "Niveau gratuit", + "billingWarningOverLimit": "Attention : Vous avez dépassé une ou plusieurs limites d'utilisation. Vos sites ne se connecteront pas tant que vous n'avez pas modifié votre abonnement ou ajusté votre utilisation.", + "billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation", + "billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@fossorial.io.", + "billingDataUsage": "Utilisation des données", + "billingOnlineTime": "Temps en ligne du site", + "billingUsers": "Utilisateurs actifs", + "billingDomains": "Domaines actifs", + "billingRemoteExitNodes": "Nœuds auto-hébergés actifs", + "billingNoLimitConfigured": "Aucune limite configurée", + "billingEstimatedPeriod": "Période de facturation estimée", + "billingIncludedUsage": "Utilisation incluse", + "billingIncludedUsageDescription": "Utilisation incluse dans votre plan d'abonnement actuel", + "billingFreeTierIncludedUsage": "Tolérances d'utilisation du niveau gratuit", + "billingIncluded": "inclus", + "billingEstimatedTotal": "Total estimé :", "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "billingEstimateNote": "Ceci est une estimation basée sur votre utilisation actuelle.", + "billingActualChargesMayVary": "Les frais réels peuvent varier.", + "billingBilledAtEnd": "Vous serez facturé à la fin de la période de facturation.", + "billingModifySubscription": "Modifier l'abonnement", + "billingStartSubscription": "Démarrer l'abonnement", + "billingRecurringCharge": "Frais récurrents", + "billingManageSubscriptionSettings": "Gérez les paramètres et préférences de votre abonnement", + "billingNoActiveSubscription": "Vous n'avez pas d'abonnement actif. Commencez votre abonnement pour augmenter les limites d'utilisation.", + "billingFailedToLoadSubscription": "Échec du chargement de l'abonnement", + "billingFailedToLoadUsage": "Échec du chargement de l'utilisation", + "billingFailedToGetCheckoutUrl": "Échec pour obtenir l'URL de paiement", + "billingPleaseTryAgainLater": "Veuillez réessayer plus tard.", + "billingCheckoutError": "Erreur de paiement", + "billingFailedToGetPortalUrl": "Échec pour obtenir l'URL du portail", + "billingPortalError": "Erreur du portail", + "billingDataUsageInfo": "Vous êtes facturé pour toutes les données transférées via vos tunnels sécurisés lorsque vous êtes connecté au cloud. Cela inclut le trafic entrant et sortant sur tous vos sites. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre plan ou réduisiez l'utilisation. Les données ne sont pas facturées lors de l'utilisation de nœuds.", + "billingOnlineTimeInfo": "Vous êtes facturé en fonction de la durée de connexion de vos sites au cloud. Par exemple, 44 640 minutes équivaut à un site fonctionnant 24/7 pendant un mois complet. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre forfait ou réduisiez votre consommation. Le temps n'est pas facturé lors de l'utilisation de nœuds.", + "billingUsersInfo": "Vous êtes facturé pour chaque utilisateur dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes utilisateurs actifs dans votre organisation.", + "billingDomainInfo": "Vous êtes facturé pour chaque domaine dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes de domaine actifs dans votre organisation.", + "billingRemoteExitNodesInfo": "Vous êtes facturé pour chaque nœud géré dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.", "domainNotFound": "Domaine introuvable", "domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.", "failed": "Échec", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.", "resourcePortRequired": "Le numéro de port est requis pour les ressources non-HTTP", "resourcePortNotAllowed": "Le numéro de port ne doit pas être défini pour les ressources HTTP", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Calculateur de prix", "signUpTerms": { "IAgreeToThe": "Je suis d'accord avec", "termsOfService": "les conditions d'utilisation", @@ -1412,41 +1412,41 @@ "addNewTarget": "Ajouter une nouvelle cible", "targetsList": "Liste des cibles", "targetErrorDuplicateTargetFound": "Cible en double trouvée", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", + "healthCheckHealthy": "Sain", + "healthCheckUnhealthy": "En mauvaise santé", + "healthCheckUnknown": "Inconnu", + "healthCheck": "Vérification de l'état de santé", + "configureHealthCheck": "Configurer la vérification de l'état de santé", + "configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}", + "enableHealthChecks": "Activer les vérifications de santé", + "enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.", + "healthScheme": "Méthode", + "healthSelectScheme": "Sélectionnez la méthode", + "healthCheckPath": "Chemin d'accès", + "healthHostname": "IP / Hôte", "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckPathDescription": "Le chemin à vérifier pour le statut de santé.", + "healthyIntervalSeconds": "Intervalle sain", + "unhealthyIntervalSeconds": "Intervalle en mauvaise santé", + "IntervalSeconds": "Intervalle sain", + "timeoutSeconds": "Délai", + "timeIsInSeconds": "Le temps est exprimé en secondes", + "retryAttempts": "Tentatives de réessai", + "expectedResponseCodes": "Codes de réponse attendus", + "expectedResponseCodesDescription": "Code de statut HTTP indiquant un état de santé satisfaisant. Si non renseigné, 200-300 est considéré comme satisfaisant.", "customHeaders": "En-têtes personnalisés", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "En-têtes séparés par une nouvelle ligne: En-nom: valeur", + "headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.", + "saveHealthCheck": "Sauvegarder la vérification de l'état de santé", + "healthCheckSaved": "Vérification de l'état de santé enregistrée", + "healthCheckSavedDescription": "La configuration de la vérification de l'état de santé a été enregistrée avec succès", + "healthCheckError": "Erreur de vérification de l'état de santé", + "healthCheckErrorDescription": "Une erreur s'est produite lors de l'enregistrement de la configuration de la vérification de l'état de santé", + "healthCheckPathRequired": "Le chemin de vérification de l'état de santé est requis", + "healthCheckMethodRequired": "La méthode HTTP est requise", + "healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes", + "healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde", + "healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1", "httpMethod": "Méthode HTTP", "selectHttpMethod": "Sélectionnez la méthode HTTP", "domainPickerSubdomainLabel": "Sous-domaine", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Entrez un sous-domaine pour rechercher et sélectionner parmi les domaines gratuits disponibles.", "domainPickerFreeDomains": "Domaines gratuits", "domainPickerSearchForAvailableDomains": "Rechercher des domaines disponibles", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Remarque : Les domaines fournis gratuitement ne sont pas disponibles pour les instances auto-hébergées pour le moment.", "resourceDomain": "Domaine", "resourceEditDomain": "Modifier le domaine", "siteName": "Nom du site", @@ -1543,72 +1543,72 @@ "autoLoginError": "Erreur de connexion automatique", "autoLoginErrorNoRedirectUrl": "Aucune URL de redirection reçue du fournisseur d'identité.", "autoLoginErrorGeneratingUrl": "Échec de la génération de l'URL d'authentification.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Gérer auto-hébergé", + "remoteExitNodeDescription": "Gérer les nœuds pour étendre votre connectivité réseau", + "remoteExitNodes": "Nœuds", + "searchRemoteExitNodes": "Rechercher des nœuds...", + "remoteExitNodeAdd": "Ajouter un noeud", + "remoteExitNodeErrorDelete": "Erreur lors de la suppression du noeud", + "remoteExitNodeQuestionRemove": "Êtes-vous sûr de vouloir supprimer le noeud {selectedNode} de l'organisation ?", + "remoteExitNodeMessageRemove": "Une fois supprimé, le noeud ne sera plus accessible.", + "remoteExitNodeMessageConfirm": "Pour confirmer, veuillez saisir le nom du noeud ci-dessous.", + "remoteExitNodeConfirmDelete": "Confirmer la suppression du noeud", + "remoteExitNodeDelete": "Supprimer le noeud", + "sidebarRemoteExitNodes": "Nœuds", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Créer un noeud", + "description": "Créer un nouveau nœud pour étendre votre connectivité réseau", + "viewAllButton": "Voir tous les nœuds", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Stratégie de création", + "description": "Choisissez ceci pour configurer manuellement votre nœud ou générer de nouveaux identifiants.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adopter un nœud", + "description": "Choisissez ceci si vous avez déjà les identifiants pour le noeud." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Générer des clés", + "description": "Choisissez ceci si vous voulez générer de nouvelles clés pour le noeud" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", + "title": "Adopter un nœud existant", + "description": "Entrez les identifiants du noeud existant que vous souhaitez adopter", + "nodeIdLabel": "Nœud ID", + "nodeIdDescription": "L'ID du noeud existant que vous voulez adopter", "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "secretDescription": "La clé secrète du noeud existant", + "submitButton": "Noeud d'Adopt" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", + "title": "Informations d'identification générées", + "description": "Utilisez ces identifiants générés pour configurer votre noeud", + "nodeIdTitle": "Nœud ID", "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "saveCredentialsTitle": "Ajouter des identifiants à la config", + "saveCredentialsDescription": "Ajoutez ces informations d'identification à votre fichier de configuration du nœud Pangolin auto-hébergé pour compléter la connexion.", + "submitButton": "Créer un noeud" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "ID de nœud et secret sont requis lors de l'adoption d'un noeud existant" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Échec du chargement des valeurs par défaut", + "defaultsNotLoaded": "Valeurs par défaut non chargées", + "createFailed": "Impossible de créer le noeud" }, "success": { - "created": "Node created successfully" + "created": "Noeud créé avec succès" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Sélection du noeud", + "remoteExitNodeSelectionDescription": "Sélectionnez un nœud pour acheminer le trafic pour ce site local", + "remoteExitNodeRequired": "Un noeud doit être sélectionné pour les sites locaux", + "noRemoteExitNodesAvailable": "Aucun noeud disponible", + "noRemoteExitNodesAvailableDescription": "Aucun noeud n'est disponible pour cette organisation. Créez d'abord un noeud pour utiliser des sites locaux.", + "exitNode": "Nœud de sortie", + "country": "Pays", + "rulesMatchCountry": "Actuellement basé sur l'IP source", "managedSelfHosted": { "title": "Gestion autonome", "description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Domaine international détecté", "willbestoredas": "Sera stocké comme :", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", + "roleMappingDescription": "Détermine comment les rôles sont assignés aux utilisateurs lorsqu'ils se connectent lorsque la fourniture automatique est activée.", + "selectRole": "Sélectionnez un rôle", "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "selectRolePlaceholder": "Choisir un rôle", + "selectRoleDescription": "Sélectionnez un rôle à assigner à tous les utilisateurs de ce fournisseur d'identité", + "roleMappingExpressionDescription": "Entrez une expression JMESPath pour extraire les informations du rôle du jeton ID", + "idpTenantIdRequired": "L'ID du locataire est requis", + "invalidValue": "Valeur non valide", + "idpTypeLabel": "Type de fournisseur d'identité", + "roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'", + "idpGoogleConfiguration": "Configuration Google", + "idpGoogleConfigurationDescription": "Configurer vos identifiants Google OAuth2", + "idpGoogleClientIdDescription": "Votre identifiant client Google OAuth2", + "idpGoogleClientSecretDescription": "Votre secret client Google OAuth2", + "idpAzureConfiguration": "Configuration de l'entra ID Azure", + "idpAzureConfigurationDescription": "Configurer vos identifiants OAuth2 Azure Entra", + "idpTenantId": "ID du locataire", + "idpTenantIdPlaceholder": "votre-locataire-id", + "idpAzureTenantIdDescription": "Votre ID de locataire Azure (trouvé dans l'aperçu Azure Active Directory)", + "idpAzureClientIdDescription": "Votre ID client d'enregistrement de l'application Azure", + "idpAzureClientSecretDescription": "Le secret de votre client d'enregistrement Azure App", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Configuration Google", + "idpAzureConfigurationTitle": "Configuration de l'entra ID Azure", + "idpTenantIdLabel": "ID du locataire", + "idpAzureClientIdDescription2": "Votre ID client d'enregistrement de l'application Azure", + "idpAzureClientSecretDescription2": "Le secret de votre client d'enregistrement Azure App", "idpGoogleDescription": "Fournisseur Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Sous-réseau", + "subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.", + "authPage": "Page d'authentification", + "authPageDescription": "Configurer la page d'authentification de votre organisation", + "authPageDomain": "Domaine de la page d'authentification", + "noDomainSet": "Aucun domaine défini", + "changeDomain": "Changer de domaine", + "selectDomain": "Sélectionner un domaine", + "restartCertificate": "Redémarrer le certificat", + "editAuthPageDomain": "Modifier le domaine de la page d'authentification", + "setAuthPageDomain": "Définir le domaine de la page d'authentification", + "failedToFetchCertificate": "Impossible de récupérer le certificat", + "failedToRestartCertificate": "Échec du redémarrage du certificat", + "addDomainToEnableCustomAuthPages": "Ajouter un domaine pour activer les pages d'authentification personnalisées pour votre organisation", + "selectDomainForOrgAuthPage": "Sélectionnez un domaine pour la page d'authentification de l'organisation", "domainPickerProvidedDomain": "Domaine fourni", "domainPickerFreeProvidedDomain": "Domaine fourni gratuitement", "domainPickerVerified": "Vérifié", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "La «{sub}» n'a pas pu être validée pour {domain}.", "domainPickerSubdomainSanitized": "Sous-domaine nettoyé", "domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Connectez-vous à votre organisation", + "orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer", + "orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.", + "orgAuthSignInWithPangolin": "Se connecter avec Pangolin", + "subscriptionRequiredToUse": "Un abonnement est requis pour utiliser cette fonctionnalité.", + "idpDisabled": "Les fournisseurs d'identité sont désactivés.", + "orgAuthPageDisabled": "La page d'authentification de l'organisation est désactivée.", + "domainRestartedDescription": "La vérification du domaine a été redémarrée avec succès", "resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml", "emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", "twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Une erreur s'est produite lors de la mise à jour de la page d\u000027authentification", + "authPageUpdated": "Page d\u000027authentification mise à jour avec succès", + "healthCheckNotAvailable": "Locale", + "rewritePath": "Réécrire le chemin", + "rewritePathDescription": "Réécrivez éventuellement le chemin avant de le transmettre à la cible." } From 418120196f63206508944e2db76241dc6773e0dc Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:55 -0700 Subject: [PATCH 143/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 374 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index ba407ff2..29915cbe 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Най-лесният начин да създадете входна точка в мрежата си. Без допълнително конфигуриране.", "siteWg": "Основен WireGuard", "siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Използвайте всеки WireGuard клиент за установяване на тунел. Ръчно нат задаване е необходимо. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", "siteLocalDescription": "Local resources only. No tunneling. ONLY WORKS ON SELF HOSTED NODES", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Само локални ресурси. Без тунелиране. РАБОТИ САМО НА СОБСТВЕНИ УЗЛИ.", "siteSeeAll": "Вижте всички сайтове", "siteTunnelDescription": "Определете как искате да се свържете с вашия сайт", "siteNewtCredentials": "Newt Удостоверения", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS ресурс", "resourceHTTPDescription": "Прокси заявки към вашето приложение през HTTPS с помощта на субдомейн или базов домейн.", "resourceRaw": "Суров TCP/UDP ресурс", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Прокси заявки към вашето приложение през TCP/UDP с помощта на номер на порт.", "resourceCreate": "Създайте ресурс", "resourceCreateDescription": "Следвайте стъпките по-долу, за да създадете нов ресурс", "resourceSeeAll": "Вижте всички ресурси", @@ -168,9 +168,9 @@ "siteSelect": "Изберете сайт", "siteSearch": "Търсене на сайт", "siteNotFound": "Няма намерени сайтове.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Изберете държава", + "searchCountries": "Търсене на държави...", + "noCountryFound": "Не е намерена държава.", "siteSelectionDescription": "Този сайт ще осигури свързаност до целта.", "resourceType": "Тип ресурс", "resourceTypeDescription": "Определете как искате да получите достъп до вашия ресурс", @@ -833,7 +833,7 @@ "emailVerifyResendProgress": "Пренасочване...", "emailVerifyResend": "Не получихте код? Кликнете тук, за да изпратите отново", "passwordNotMatch": "Паролите не съвпадат", - "signupError": "An error occurred while signing up", + "signupError": "Възникна грешка при регистрация", "pangolinLogoAlt": "Лого на Pangolin", "inviteAlready": "Изглежда, че сте били поканени!", "inviteAlreadyDescription": "За да приемете поканата, трябва да влезете или да създадете акаунт.", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Поддомейн: {subdomain}", "domainPickerNamespace": "Име на пространство: {namespace}", "domainPickerShowMore": "Покажи повече", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Избор на регион", + "regionSelectorInfo": "Изборът на регион ни помага да предоставим по-добра производителност за вашето местоположение. Не е необходимо да сте в същия регион като сървъра.", + "regionSelectorPlaceholder": "Изберете регион", + "regionSelectorComingSoon": "Очаква се скоро", + "billingLoadingSubscription": "Зареждане на абонамент...", + "billingFreeTier": "Безплатен план", + "billingWarningOverLimit": "Предупреждение: Превишили сте една или повече лимити за използване. Вашите сайтове няма да се свържат, докато не промените абонамента си или не коригирате използването.", + "billingUsageLimitsOverview": "Преглед на лимитите за използване", + "billingMonitorUsage": "Следете използването спрямо конфигурираните лимити. Ако ви е необходимо увеличаване на лимитите, моля, свържете се с нас на support@fossorial.io.", + "billingDataUsage": "Използване на данни", + "billingOnlineTime": "Време на работа на сайта", + "billingUsers": "Активни потребители", + "billingDomains": "Активни домейни", + "billingRemoteExitNodes": "Активни самостоятелно хоствани възли", + "billingNoLimitConfigured": "Няма конфигуриран лимит", + "billingEstimatedPeriod": "Очакван период на фактуриране", + "billingIncludedUsage": "Включено използване", + "billingIncludedUsageDescription": "Използване, включено във вашия текущ абонаментен план", + "billingFreeTierIncludedUsage": "Разрешени използвания в безплатния план", + "billingIncluded": "включено", + "billingEstimatedTotal": "Очаквана сума:", + "billingNotes": "Бележки", + "billingEstimateNote": "Това е приблизителна оценка, основана на текущото ви използване.", + "billingActualChargesMayVary": "Реалните разходи могат да варират.", + "billingBilledAtEnd": "Фактурирате се в края на фактурния период.", + "billingModifySubscription": "Промяна на абонамента", + "billingStartSubscription": "Започване на абонамент", + "billingRecurringCharge": "Повтаряща се такса", + "billingManageSubscriptionSettings": "Управление на настройките и предпочитанията на абонамента ви", + "billingNoActiveSubscription": "Нямате активен абонамент. Започнете абонамента си, за да увеличите лимитите за използване.", + "billingFailedToLoadSubscription": "Грешка при зареждане на абонамент", + "billingFailedToLoadUsage": "Грешка при зареждане на използването", + "billingFailedToGetCheckoutUrl": "Неуспех при получаване на URL за плащане", + "billingPleaseTryAgainLater": "Моля, опитайте отново по-късно.", + "billingCheckoutError": "Грешка при плащане", + "billingFailedToGetPortalUrl": "Неуспех при получаване на URL на портала", + "billingPortalError": "Грешка в портала", + "billingDataUsageInfo": "Таксува се за всички данни, прехвърляни през вашите защитени тунели, когато сте свързани към облака. Това включва както входящия, така и изходящия трафик за всички ваши сайтове. Когато достигнете лимита си, вашите сайтове ще бъдат прекъснати, докато не надстроите плана или не намалите използването. Данните не се таксуват при използване на възли.", + "billingOnlineTimeInfo": "Таксува се на база колко време вашите сайтове остават свързани с облака. Пример: 44,640 минути се равняват на един сайт работещ 24/7 за цял месец. Когато достигнете лимита си, вашите сайтове ще бъдат прекъснати, докато не надстроите плана или не намалите използването. Времето не се таксува при използване на възли.", + "billingUsersInfo": "Таксува се за всеки потребител във вашата организация. Фактурирането се извършва ежедневно на базата на броя активни потребителски акаунти във вашата организация.", + "billingDomainInfo": "Таксува се за всеки домейн във вашата организация. Фактурирането се извършва ежедневно на базата на броя активни домейн акаунти във вашата организация.", + "billingRemoteExitNodesInfo": "Таксува се за всеки управляван възел във вашата организация. Фактурирането се извършва ежедневно на базата на броя активни управлявани възли във вашата организация.", "domainNotFound": "Домейнът не е намерен", "domainNotFoundDescription": "Този ресурс е деактивиран, защото домейнът вече не съществува в нашата система. Моля, задайте нов домейн за този ресурс.", "failed": "Неуспешно", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Промените в DNS може да отнемат време, за да се разпространят в интернет. Това може да отнеме от няколко минути до 48 часа, в зависимост от вашия DNS доставчик и TTL настройките .", "resourcePortRequired": "Номерът на порта е задължителен за не-HTTP ресурси", "resourcePortNotAllowed": "Номерът на порта не трябва да бъде задаван за HTTP ресурси", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Калкулатор на цените", "signUpTerms": { "IAgreeToThe": "Съгласен съм с", "termsOfService": "условията за ползване", @@ -1412,41 +1412,41 @@ "addNewTarget": "Добави нова цел", "targetsList": "Списък с цели", "targetErrorDuplicateTargetFound": "Дублирана цел намерена", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Здрав", + "healthCheckUnhealthy": "Нездрав", + "healthCheckUnknown": "Неизвестен", + "healthCheck": "Проверка на здравето", + "configureHealthCheck": "Конфигуриране на проверка на здравето", + "configureHealthCheckDescription": "Настройте мониторинг на здравето за {target}", + "enableHealthChecks": "Разрешаване на проверки на здравето", + "enableHealthChecksDescription": "Мониторинг на здравето на тази цел. Можете да наблюдавате различен краен пункт от целта, ако е необходимо.", + "healthScheme": "Метод", + "healthSelectScheme": "Избор на метод", + "healthCheckPath": "Път", + "healthHostname": "IP / Хост", + "healthPort": "Порт", + "healthCheckPathDescription": "Пътят за проверка на здравното състояние.", + "healthyIntervalSeconds": "Интервал за здраве", + "unhealthyIntervalSeconds": "Интервал за нездраве", + "IntervalSeconds": "Интервал за здраве", + "timeoutSeconds": "Време за изчакване", + "timeIsInSeconds": "Времето е в секунди", + "retryAttempts": "Опити за повторно", + "expectedResponseCodes": "Очаквани кодове за отговор", + "expectedResponseCodesDescription": "HTTP статус код, указващ здравословно състояние. Ако бъде оставено празно, между 200-300 се счита за здравословно.", "customHeaders": "Персонализирани заглавия", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Add custom headers to be sent when proxying requests. One per line in the format Header-Name: value", + "headersValidationError": "Заглавията трябва да бъдат във формат: Име на заглавието: стойност.", + "saveHealthCheck": "Запазване на проверка на здравето", + "healthCheckSaved": "Проверка на здравето е запазена", + "healthCheckSavedDescription": "Конфигурацията на здравната проверка е запазена успешно", + "healthCheckError": "Грешка при проверката на здравето", + "healthCheckErrorDescription": "Възникна грешка при запазването на конфигурацията за проверка на здравето", + "healthCheckPathRequired": "Изисква се път за проверка на здравето", + "healthCheckMethodRequired": "Изисква се HTTP метод", + "healthCheckIntervalMin": "Интервалът за проверка трябва да е поне 5 секунди", + "healthCheckTimeoutMin": "Времето за изчакване трябва да е поне 1 секунда", + "healthCheckRetryMin": "Опитите за повторение трябва да са поне 1", "httpMethod": "HTTP Метод", "selectHttpMethod": "Изберете HTTP метод", "domainPickerSubdomainLabel": "Поддомен", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Въведете поддомен, за да търсите и изберете от наличните свободни домейни.", "domainPickerFreeDomains": "Безплатни домейни", "domainPickerSearchForAvailableDomains": "Търсене за налични домейни", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Забележка: Безплатните предоставени домейни не са налични за самостоятелно хоствани инстанции в момента.", "resourceDomain": "Домейн", "resourceEditDomain": "Редактиране на домейн", "siteName": "Име на сайта", @@ -1543,72 +1543,72 @@ "autoLoginError": "Грешка при автоматично влизане", "autoLoginErrorNoRedirectUrl": "Не е получен URL за пренасочване от доставчика на идентификационни данни.", "autoLoginErrorGeneratingUrl": "Неуспешно генериране на URL за удостоверяване.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Управление на самостоятелно хоствани", + "remoteExitNodeDescription": "Управление на възли за разширяване на мрежовата ви свързаност", + "remoteExitNodes": "Възли", + "searchRemoteExitNodes": "Търсене на възли...", + "remoteExitNodeAdd": "Добавяне на възел", + "remoteExitNodeErrorDelete": "Грешка при изтриване на възел", + "remoteExitNodeQuestionRemove": "Сигурни ли сте, че искате да премахнете възела {selectedNode} от организацията?", + "remoteExitNodeMessageRemove": "След премахване, възелът вече няма да бъде достъпен.", + "remoteExitNodeMessageConfirm": "За потвърждение, моля въведете името на възела по-долу.", + "remoteExitNodeConfirmDelete": "Потвърдете изтриването на възела (\"Confirm Delete Site\" match)", + "remoteExitNodeDelete": "Изтрийте възела (\"Delete Site\" match)", + "sidebarRemoteExitNodes": "Възли (\"Local\" match)", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Създаване на възел", + "description": "Създайте нов възел, за да разширите мрежовата си свързаност", + "viewAllButton": "Вижте всички възли", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Стратегия на създаване", + "description": "Изберете това, за да конфигурирате ръчно възела си или да създадете нови кредити.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Осиновете възел", + "description": "Изберете това, ако вече имате кредити за възела." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Генериране на ключове", + "description": "Изберете това, ако искате да генерирате нови ключове за възела" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Осиновяване на съществуващ възел", + "description": "Въведете данните на съществуващия възел, който искате да осиновите", + "nodeIdLabel": "ID на възела", + "nodeIdDescription": "ID на съществуващия възел, който искате да осиновите", + "secretLabel": "Секретен", + "secretDescription": "Секретният ключ на съществуващия възел", + "submitButton": "Осиновете възела" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Генерирани кредити", + "description": "Използвайте тези генерирани кредити, за да конфигурирате възела си", + "nodeIdTitle": "ID на възела", + "secretTitle": "Секретен", + "saveCredentialsTitle": "Добавете кредити към конфигурацията", + "saveCredentialsDescription": "Добавете тези кредити към конфигурационния файл на вашия самостоятелно хостван Pangolin възел, за да завършите връзката.", + "submitButton": "Създаване на възел" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "ID на възела и секрет са необходими при осиновяване на съществуващ възел" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Грешка при зареждане на подразбирани настройки", + "defaultsNotLoaded": "Подразбирани настройки не са заредени", + "createFailed": "Грешка при създаване на възел" }, "success": { - "created": "Node created successfully" + "created": "Възелът е създаден успешно" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Избор на възел", + "remoteExitNodeSelectionDescription": "Изберете възел, през който да пренасочвате трафика за местния сайт", + "remoteExitNodeRequired": "Необходимо е да бъде избран възел за местни сайтове", + "noRemoteExitNodesAvailable": "Няма налични възли", + "noRemoteExitNodesAvailableDescription": "Няма налични възли за тази организация. Първо създайте възел, за да използвате местни сайтове.", + "exitNode": "Изходен възел", + "country": "Държава", + "rulesMatchCountry": "Понастоящем на базата на изходния IP", "managedSelfHosted": { "title": "Управлявано Самостоятелно-хоствано", "description": "По-надежден и по-нисък поддръжка на Самостоятелно-хостван Панголиин сървър с допълнителни екстри", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Открит международен домейн", "willbestoredas": "Ще бъде съхранено като:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Определете как се разпределят ролите на потребителите при вписване, когато е активирано автоматично предоставяне.", + "selectRole": "Избор на роля", + "roleMappingExpression": "Израз", + "selectRolePlaceholder": "Избор на роля", + "selectRoleDescription": "Изберете роля за присвояване на всички потребители от този доставчик на идентичност", + "roleMappingExpressionDescription": "Въведете израз JMESPath, за да извлечете информация за ролята от ID токена", + "idpTenantIdRequired": "Изисква се идентификационен номер на наемателя", + "invalidValue": "Невалидна стойност", + "idpTypeLabel": "Тип на доставчика на идентичност", + "roleMappingExpressionPlaceholder": "напр.: contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Конфигурация на Google", + "idpGoogleConfigurationDescription": "Конфигурирайте своите Google OAuth2 кредити", + "idpGoogleClientIdDescription": "Вашият Google OAuth2 клиентски ID", + "idpGoogleClientSecretDescription": "Вашият Google OAuth2 клиентски секрет", + "idpAzureConfiguration": "Конфигурация на Azure Entra ID", + "idpAzureConfigurationDescription": "Конфигурирайте своите Azure Entra ID OAuth2 кредити", + "idpTenantId": "Идентификационен номер на наемателя", + "idpTenantIdPlaceholder": "вашият идентификационен номер на наемателя", + "idpAzureTenantIdDescription": "Вашият Azure идентификационен номер на наемателя (намира се в преглед на Azure Active Directory)", + "idpAzureClientIdDescription": "Вашият Azure клиентски идентификационен номер за приложението", + "idpAzureClientSecretDescription": "Вашият Azure клиентски секрет за приложението", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Конфигурация на Google", + "idpAzureConfigurationTitle": "Конфигурация на Azure Entra ID", + "idpTenantIdLabel": "Идентификационен номер на наемателя", + "idpAzureClientIdDescription2": "Вашият Azure клиентски идентификационен номер за приложението", + "idpAzureClientSecretDescription2": "Вашият Azure клиентски секрет за приложението", "idpGoogleDescription": "Google OAuth2/OIDC доставчик", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC доставчик", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Подмрежа", + "subnetDescription": "Подмрежата за конфигурацията на мрежата на тази организация.", + "authPage": "Страница за удостоверяване", + "authPageDescription": "Конфигурирайте страницата за удостоверяване на вашата организация", + "authPageDomain": "Домен на страницата за удостоверяване", + "noDomainSet": "Няма зададен домейн", + "changeDomain": "Смяна на домейн", + "selectDomain": "Избор на домейн", + "restartCertificate": "Рестартиране на сертификат", + "editAuthPageDomain": "Редактиране на домейна на страницата за удостоверяване", + "setAuthPageDomain": "Задаване на домейн на страницата за удостоверяване", + "failedToFetchCertificate": "Неуспех при извличане на сертификат", + "failedToRestartCertificate": "Неуспех при рестартиране на сертификат", + "addDomainToEnableCustomAuthPages": "Добавете домейн, за да активирате персонализирани страници за удостоверяване за вашата организация", + "selectDomainForOrgAuthPage": "Изберете домейн за страницата за удостоверяване на организацията", "domainPickerProvidedDomain": "Предоставен домейн", "domainPickerFreeProvidedDomain": "Безплатен предоставен домейн", "domainPickerVerified": "Проверено", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не може да се направи валиден за {domain}.", "domainPickerSubdomainSanitized": "Поддомен пречистен", "domainPickerSubdomainCorrected": "\"{sub}\" беше коригиран на \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Впишете се във вашата организация", + "orgAuthChooseIdpDescription": "Изберете своя доставчик на идентичност, за да продължите", + "orgAuthNoIdpConfigured": "Тази организация няма конфигурирани доставчици на идентичност. Можете да влезете с вашата Pangolin идентичност.", + "orgAuthSignInWithPangolin": "Впишете се с Pangolin", + "subscriptionRequiredToUse": "Необходим е абонамент, за да използвате тази функция.", + "idpDisabled": "Доставчиците на идентичност са деактивирани.", + "orgAuthPageDisabled": "Страницата за удостоверяване на организацията е деактивирана.", + "domainRestartedDescription": "Проверка на домейна е рестартирана успешно", "resourceAddEntrypointsEditFile": "Редактиране на файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактиране на файл: docker-compose.yml", "emailVerificationRequired": "Потвърждението на Email е необходимо. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", "twoFactorSetupRequired": "Необходима е настройка на двуфакторно удостоверяване. Моля, влезте отново чрез {dashboardUrl}/auth/login, за да завършите тази стъпка. След това, върнете се тук.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Възникна грешка при актуализирането на настройките на страницата за удостоверяване", + "authPageUpdated": "Страницата за удостоверяване е актуализирана успешно", + "healthCheckNotAvailable": "Локална", + "rewritePath": "Пренапиши път", + "rewritePathDescription": "По избор пренапиши пътя преди пренасочване към целта." } From 1251b1e87067e3d0eae14acd892ae6d21036814e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:56 -0700 Subject: [PATCH 144/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 372 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index fb6e2eb0..ab264302 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Nejjednodušší způsob, jak vytvořit vstupní bod do vaší sítě. Žádné další nastavení.", "siteWg": "Základní WireGuard", "siteWgDescription": "Použijte jakéhokoli klienta WireGuard abyste sestavili tunel. Vyžaduje se ruční nastavení NAT.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Použijte jakéhokoli klienta WireGuard abyste sestavili tunel. Vyžaduje se ruční nastavení NAT. FUNGUJE POUZE NA SELF-HOSTED SERVERECH", "siteLocalDescription": "Pouze lokální zdroje. Žádný tunel.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Pouze lokální zdroje. Žádný tunel. FUNGUJE POUZE NA SELF-HOSTED SERVERECH", "siteSeeAll": "Zobrazit všechny lokality", "siteTunnelDescription": "Určete jak se chcete připojit k vaší lokalitě", "siteNewtCredentials": "Přihlašovací údaje Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Zdroj HTTPS", "resourceHTTPDescription": "Požadavky na proxy pro vaši aplikaci přes HTTPS pomocí subdomény nebo základní domény.", "resourceRaw": "Surový TCP/UDP zdroj", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Požadavky na proxy pro vaši aplikaci přes TCP/UDP pomocí čísla portu.", "resourceCreate": "Vytvořit zdroj", "resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj", "resourceSeeAll": "Zobrazit všechny zdroje", @@ -168,9 +168,9 @@ "siteSelect": "Vybrat lokalitu", "siteSearch": "Hledat lokalitu", "siteNotFound": "Nebyla nalezena žádná lokalita.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Vyberte zemi", + "searchCountries": "Hledat země...", + "noCountryFound": "Nebyla nalezena žádná země.", "siteSelectionDescription": "Tato lokalita poskytne připojení k cíli.", "resourceType": "Typ zdroje", "resourceTypeDescription": "Určete, jak chcete přistupovat ke svému zdroji", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdoména: {subdomain}", "domainPickerNamespace": "Jmenný prostor: {namespace}", "domainPickerShowMore": "Zobrazit více", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Vybrat region", + "regionSelectorInfo": "Výběr regionu nám pomáhá poskytovat lepší výkon pro vaši polohu. Nemusíte být ve stejném regionu jako váš server.", + "regionSelectorPlaceholder": "Vyberte region", + "regionSelectorComingSoon": "Již brzy", + "billingLoadingSubscription": "Načítání odběru...", + "billingFreeTier": "Volná úroveň", + "billingWarningOverLimit": "Upozornění: Překročili jste jeden nebo více omezení používání. Vaše stránky se nepřipojí dokud nezměníte předplatné nebo neupravíte své používání.", + "billingUsageLimitsOverview": "Přehled omezení použití", + "billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@fossorial.io.", + "billingDataUsage": "Využití dat", + "billingOnlineTime": "Stránka online čas", + "billingUsers": "Aktivní uživatelé", + "billingDomains": "Aktivní domény", + "billingRemoteExitNodes": "Aktivní Samostatně hostované uzly", + "billingNoLimitConfigured": "Žádný limit nenastaven", + "billingEstimatedPeriod": "Odhadované období fakturace", + "billingIncludedUsage": "Zahrnuto využití", + "billingIncludedUsageDescription": "Využití zahrnované s aktuálním plánem předplatného", + "billingFreeTierIncludedUsage": "Povolenky bezplatné úrovně využití", + "billingIncluded": "zahrnuto", + "billingEstimatedTotal": "Odhadovaný celkem:", + "billingNotes": "Poznámky", + "billingEstimateNote": "Toto je odhad založený na aktuálním využití.", + "billingActualChargesMayVary": "Skutečné náklady se mohou lišit.", + "billingBilledAtEnd": "Budete účtováni na konci fakturační doby.", + "billingModifySubscription": "Upravit předplatné", + "billingStartSubscription": "Začít předplatné", + "billingRecurringCharge": "Opakované nabití", + "billingManageSubscriptionSettings": "Spravovat nastavení a nastavení předplatného", + "billingNoActiveSubscription": "Nemáte aktivní předplatné. Začněte předplatné, abyste zvýšili omezení používání.", + "billingFailedToLoadSubscription": "Nepodařilo se načíst odběr", + "billingFailedToLoadUsage": "Nepodařilo se načíst využití", + "billingFailedToGetCheckoutUrl": "Nepodařilo se získat adresu URL pokladny", + "billingPleaseTryAgainLater": "Zkuste to prosím znovu později.", + "billingCheckoutError": "Chyba pokladny", + "billingFailedToGetPortalUrl": "Nepodařilo se získat URL portálu", + "billingPortalError": "Chyba portálu", + "billingDataUsageInfo": "Pokud jste připojeni k cloudu, jsou vám účtována všechna data přenášená prostřednictvím zabezpečených tunelů. To zahrnuje příchozí i odchozí provoz na všech vašich stránkách. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezmenšíte jeho používání. Data nejsou nabírána při používání uzlů.", + "billingOnlineTimeInfo": "Platíte na základě toho, jak dlouho budou vaše stránky připojeny k cloudu. Například, 44,640 minut se rovná jedné stránce 24/7 po celý měsíc. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezkrátíte jeho používání. Čas není vybírán při používání uzlů.", + "billingUsersInfo": "Obdrželi jste platbu za každého uživatele ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních uživatelských účtů ve vašem org.", + "billingDomainInfo": "Platba je účtována za každou doménu ve vaší organizaci. Fakturace je počítána denně na základě počtu aktivních doménových účtů na Vašem org.", + "billingRemoteExitNodesInfo": "Za každý spravovaný uzel ve vaší organizaci se vám účtuje denně. Fakturace je počítána na základě počtu aktivních spravovaných uzlů ve vašem org.", "domainNotFound": "Doména nenalezena", "domainNotFoundDescription": "Tento dokument je zakázán, protože doména již neexistuje náš systém. Nastavte prosím novou doménu pro tento dokument.", "failed": "Selhalo", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Změna DNS může trvat nějakou dobu, než se šíří po internetu. To může trvat kdekoli od několika minut do 48 hodin v závislosti na poskytovateli DNS a nastavení TTL.", "resourcePortRequired": "Pro neHTTP zdroje je vyžadováno číslo portu", "resourcePortNotAllowed": "Číslo portu by nemělo být nastaveno pro HTTP zdroje", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Cenová kalkulačka", "signUpTerms": { "IAgreeToThe": "Souhlasím s", "termsOfService": "podmínky služby", @@ -1412,41 +1412,41 @@ "addNewTarget": "Add New Target", "targetsList": "Seznam cílů", "targetErrorDuplicateTargetFound": "Byl nalezen duplicitní cíl", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Zdravé", + "healthCheckUnhealthy": "Nezdravé", + "healthCheckUnknown": "Neznámý", + "healthCheck": "Kontrola stavu", + "configureHealthCheck": "Konfigurace kontroly stavu", + "configureHealthCheckDescription": "Nastavit sledování zdravotního stavu pro {target}", + "enableHealthChecks": "Povolit kontrolu stavu", + "enableHealthChecksDescription": "Sledujte zdraví tohoto cíle. V případě potřeby můžete sledovat jiný cílový bod, než je cíl.", + "healthScheme": "Způsob", + "healthSelectScheme": "Vybrat metodu", + "healthCheckPath": "Cesta", + "healthHostname": "IP / Hostitel", + "healthPort": "Přístav", + "healthCheckPathDescription": "Cesta ke kontrole zdravotního stavu.", + "healthyIntervalSeconds": "Interval zdraví", + "unhealthyIntervalSeconds": "Nezdravý interval", + "IntervalSeconds": "Interval zdraví", + "timeoutSeconds": "Časový limit", + "timeIsInSeconds": "Čas je v sekundách", + "retryAttempts": "Opakovat pokusy", + "expectedResponseCodes": "Očekávané kódy odezvy", + "expectedResponseCodesDescription": "HTTP kód stavu, který označuje zdravý stav. Ponecháte-li prázdné, 200-300 je považováno za zdravé.", "customHeaders": "Vlastní záhlaví", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Záhlaví oddělená nová řádka: hodnota", + "headersValidationError": "Headers must be in the format: Header-Name: value.", + "saveHealthCheck": "Uložit kontrolu stavu", + "healthCheckSaved": "Kontrola stavu uložena", + "healthCheckSavedDescription": "Nastavení kontroly stavu bylo úspěšně uloženo", + "healthCheckError": "Chyba kontroly stavu", + "healthCheckErrorDescription": "Došlo k chybě při ukládání konfigurace kontroly stavu", + "healthCheckPathRequired": "Je vyžadována cesta kontroly stavu", + "healthCheckMethodRequired": "HTTP metoda je povinná", + "healthCheckIntervalMin": "Interval kontroly musí být nejméně 5 sekund", + "healthCheckTimeoutMin": "Časový limit musí být nejméně 1 sekunda", + "healthCheckRetryMin": "Pokusy opakovat musí být alespoň 1", "httpMethod": "HTTP metoda", "selectHttpMethod": "Vyberte HTTP metodu", "domainPickerSubdomainLabel": "Subdoména", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Zadejte subdoménu pro hledání a výběr z dostupných domén zdarma.", "domainPickerFreeDomains": "Volné domény", "domainPickerSearchForAvailableDomains": "Hledat dostupné domény", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Poznámka: Poskytnuté domény nejsou momentálně k dispozici pro vlastní hostované instance.", "resourceDomain": "Doména", "resourceEditDomain": "Upravit doménu", "siteName": "Název webu", @@ -1543,72 +1543,72 @@ "autoLoginError": "Automatická chyba přihlášení", "autoLoginErrorNoRedirectUrl": "Od poskytovatele identity nebyla obdržena žádná adresa URL.", "autoLoginErrorGeneratingUrl": "Nepodařilo se vygenerovat ověřovací URL.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Spravovat vlastní hostování", + "remoteExitNodeDescription": "Spravujte uzly pro rozšíření připojení k síti", + "remoteExitNodes": "Uzly", + "searchRemoteExitNodes": "Hledat uzly...", + "remoteExitNodeAdd": "Přidat uzel", + "remoteExitNodeErrorDelete": "Chyba při odstraňování uzlu", + "remoteExitNodeQuestionRemove": "Jste si jisti, že chcete odstranit uzel {selectedNode} z organizace?", + "remoteExitNodeMessageRemove": "Po odstranění uzel již nebude přístupný.", + "remoteExitNodeMessageConfirm": "Pro potvrzení zadejte název uzlu níže.", + "remoteExitNodeConfirmDelete": "Potvrdit odstranění uzlu", + "remoteExitNodeDelete": "Odstranit uzel", + "sidebarRemoteExitNodes": "Uzly", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Vytvořit uzel", + "description": "Vytvořit nový uzel pro rozšíření síťového připojení", + "viewAllButton": "Zobrazit všechny uzly", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Strategie tvorby", + "description": "Vyberte pro manuální konfiguraci vašeho uzlu nebo vygenerujte nové přihlašovací údaje.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Přijmout uzel", + "description": "Zvolte tuto možnost, pokud již máte přihlašovací údaje k uzlu." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Generovat klíče", + "description": "Vyberte tuto možnost, pokud chcete vygenerovat nové klíče pro uzel" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Přijmout existující uzel", + "description": "Zadejte přihlašovací údaje existujícího uzlu, který chcete přijmout", + "nodeIdLabel": "ID uzlu", + "nodeIdDescription": "ID existujícího uzlu, který chcete přijmout", + "secretLabel": "Tajný klíč", + "secretDescription": "Tajný klíč existujícího uzlu", + "submitButton": "Přijmout uzel" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Vygenerovaná pověření", + "description": "Použijte tyto generované přihlašovací údaje pro nastavení vašeho uzlu", + "nodeIdTitle": "ID uzlu", + "secretTitle": "Tajný klíč", + "saveCredentialsTitle": "Přidat přihlašovací údaje do konfigurace", + "saveCredentialsDescription": "Přidejte tyto přihlašovací údaje do vlastního konfiguračního souboru Pangolin uzlu pro dokončení připojení.", + "submitButton": "Vytvořit uzel" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "ID uzlu a tajný klíč jsou vyžadovány při přijetí existujícího uzlu" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Nepodařilo se načíst výchozí hodnoty", + "defaultsNotLoaded": "Výchozí hodnoty nebyly načteny", + "createFailed": "Nepodařilo se vytvořit uzel" }, "success": { - "created": "Node created successfully" + "created": "Uzel byl úspěšně vytvořen" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Výběr uzlu", + "remoteExitNodeSelectionDescription": "Vyberte uzel pro směrování provozu přes tuto lokální stránku", + "remoteExitNodeRequired": "Pro lokální stránky musí být vybrán uzel", + "noRemoteExitNodesAvailable": "Nejsou k dispozici žádné uzly", + "noRemoteExitNodesAvailableDescription": "Pro tuto organizaci nejsou k dispozici žádné uzly. Nejprve vytvořte uzel pro použití lokálních stránek.", + "exitNode": "Ukončit uzel", + "country": "L 343, 22.12.2009, s. 1).", + "rulesMatchCountry": "Aktuálně založené na zdrojové IP adrese", "managedSelfHosted": { "title": "Spravované vlastní hostování", "description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Zjištěna mezinárodní doména", "willbestoredas": "Bude uloženo jako:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Určete, jak jsou role přiřazeny uživatelům, když se přihlásí, když je povoleno automatické poskytnutí služby.", + "selectRole": "Vyberte roli", + "roleMappingExpression": "Výraz", + "selectRolePlaceholder": "Vyberte roli", + "selectRoleDescription": "Vyberte roli pro přiřazení všem uživatelům od tohoto poskytovatele identity", + "roleMappingExpressionDescription": "Zadejte výraz JMESPath pro získání informací o roli z ID token", + "idpTenantIdRequired": "ID nájemce je povinné", + "invalidValue": "Neplatná hodnota", + "idpTypeLabel": "Typ poskytovatele identity", + "roleMappingExpressionPlaceholder": "např. obsahuje(skupiny, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Konfigurace Google", + "idpGoogleConfigurationDescription": "Konfigurace přihlašovacích údajů Google OAuth2", + "idpGoogleClientIdDescription": "Vaše ID klienta Google OAuth2", + "idpGoogleClientSecretDescription": "Tajný klíč klienta Google OAuth2", + "idpAzureConfiguration": "Nastavení Azure Entra ID", + "idpAzureConfigurationDescription": "Nastavte vaše Azure Entra ID OAuth2", + "idpTenantId": "ID tenanta", + "idpTenantIdPlaceholder": "vaše-tenant-id", + "idpAzureTenantIdDescription": "Vaše Azure nájemce ID (nalezeno v přehledu Azure Active Directory – Azure)", + "idpAzureClientIdDescription": "Vaše ID registrace aplikace Azure", + "idpAzureClientSecretDescription": "Tajný klíč registrace aplikace Azure", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Konfigurace Google", + "idpAzureConfigurationTitle": "Nastavení Azure Entra ID", + "idpTenantIdLabel": "ID tenanta", + "idpAzureClientIdDescription2": "Vaše ID registrace aplikace Azure", + "idpAzureClientSecretDescription2": "Tajný klíč registrace aplikace Azure", "idpGoogleDescription": "Poskytovatel Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Podsíť", + "subnetDescription": "Podsíť pro konfiguraci sítě této organizace.", + "authPage": "Auth stránka", + "authPageDescription": "Konfigurace autentizační stránky vaší organizace", + "authPageDomain": "Doména ověření stránky", + "noDomainSet": "Není nastavena žádná doména", + "changeDomain": "Změnit doménu", + "selectDomain": "Vybrat doménu", + "restartCertificate": "Restartovat certifikát", + "editAuthPageDomain": "Upravit doménu autentizační stránky", + "setAuthPageDomain": "Nastavit doménu autentické stránky", + "failedToFetchCertificate": "Nepodařilo se načíst certifikát", + "failedToRestartCertificate": "Restartování certifikátu se nezdařilo", + "addDomainToEnableCustomAuthPages": "Přidejte doménu pro povolení vlastních ověřovacích stránek pro vaši organizaci", + "selectDomainForOrgAuthPage": "Vyberte doménu pro ověřovací stránku organizace", "domainPickerProvidedDomain": "Poskytnutá doména", "domainPickerFreeProvidedDomain": "Zdarma poskytnutá doména", "domainPickerVerified": "Ověřeno", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nemohl být platný pro {domain}.", "domainPickerSubdomainSanitized": "Upravená subdoména", "domainPickerSubdomainCorrected": "\"{sub}\" bylo opraveno na \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Přihlaste se do vaší organizace", + "orgAuthChooseIdpDescription": "Chcete-li pokračovat, vyberte svého poskytovatele identity", + "orgAuthNoIdpConfigured": "Tato organizace nemá nakonfigurovány žádné poskytovatele identity. Místo toho se můžete přihlásit s vaší Pangolinovou identitou.", + "orgAuthSignInWithPangolin": "Přihlásit se pomocí Pangolinu", + "subscriptionRequiredToUse": "Pro použití této funkce je vyžadováno předplatné.", + "idpDisabled": "Poskytovatelé identit jsou zakázáni.", + "orgAuthPageDisabled": "Ověřovací stránka organizace je zakázána.", + "domainRestartedDescription": "Ověření domény bylo úspěšně restartováno", "resourceAddEntrypointsEditFile": "Upravit soubor: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Upravit soubor: docker-compose.yml", "emailVerificationRequired": "Je vyžadováno ověření e-mailu. Přihlaste se znovu pomocí {dashboardUrl}/auth/login dokončete tento krok. Poté se vraťte zde.", "twoFactorSetupRequired": "Je vyžadováno nastavení dvoufaktorového ověřování. Přihlaste se znovu pomocí {dashboardUrl}/autentizace/přihlášení dokončí tento krok. Poté se vraťte zde.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Při aktualizaci nastavení autentizační stránky došlo k chybě", + "authPageUpdated": "Autentizační stránka byla úspěšně aktualizována", + "healthCheckNotAvailable": "Místní", + "rewritePath": "Přepsat cestu", + "rewritePathDescription": "Volitelně přepište cestu před odesláním na cíl." } From a39a133ee5c1f6a1c0cfe68ed814ec9a2e4cc138 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:57 -0700 Subject: [PATCH 145/322] New translations en-us.json (German) --- messages/de-DE.json | 380 ++++++++++++++++++++++---------------------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/messages/de-DE.json b/messages/de-DE.json index 430a7949..5ac9fc02 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.", "siteWg": "Einfacher WireGuard Tunnel", "siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu erstellen. Manuelles NAT-Setup erforderlich. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN", "siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Nur lokale Ressourcen. Keine Tunneldurchführung. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN", "siteSeeAll": "Alle Standorte anzeigen", "siteTunnelDescription": "Lege fest, wie du dich mit deinem Standort verbinden möchtest", "siteNewtCredentials": "Neue Newt Zugangsdaten", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-Ressource", "resourceHTTPDescription": "Proxy-Anfragen an Ihre App über HTTPS unter Verwendung einer Subdomain oder einer Basis-Domain.", "resourceRaw": "Rohe TCP/UDP Ressource", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Proxy-Anfragen an Ihre App über TCP/UDP mit einer Portnummer.", "resourceCreate": "Ressource erstellen", "resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen", "resourceSeeAll": "Alle Ressourcen anzeigen", @@ -168,9 +168,9 @@ "siteSelect": "Standort auswählen", "siteSearch": "Standorte durchsuchen", "siteNotFound": "Keinen Standort gefunden.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Land auswählen", + "searchCountries": "Länder suchen...", + "noCountryFound": "Kein Land gefunden.", "siteSelectionDescription": "Dieser Standort wird die Verbindung zum Ziel herstellen.", "resourceType": "Ressourcentyp", "resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten", @@ -1140,8 +1140,8 @@ "sidebarAllUsers": "Alle Benutzer", "sidebarIdentityProviders": "Identitätsanbieter", "sidebarLicense": "Lizenz", - "sidebarClients": "Clients (Beta)", - "sidebarDomains": "Domains", + "sidebarClients": "Kunden (Beta)", + "sidebarDomains": "Domänen", "enableDockerSocket": "Docker Blaupause aktivieren", "enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.", "enableDockerSocketLink": "Mehr erfahren", @@ -1189,7 +1189,7 @@ "certificateStatus": "Zertifikatsstatus", "loading": "Laden", "restart": "Neustart", - "domains": "Domains", + "domains": "Domänen", "domainsDescription": "Domains für Ihre Organisation verwalten", "domainsSearch": "Domains durchsuchen...", "domainAdd": "Domain hinzufügen", @@ -1202,7 +1202,7 @@ "domainMessageConfirm": "Um zu bestätigen, geben Sie bitte den Domainnamen unten ein.", "domainConfirmDelete": "Domain-Löschung bestätigen", "domainDelete": "Domain löschen", - "domain": "Domain", + "domain": "Domäne", "selectDomainTypeNsName": "Domain-Delegation (NS)", "selectDomainTypeNsDescription": "Diese Domain und alle ihre Subdomains. Verwenden Sie dies, wenn Sie eine gesamte Domainzone kontrollieren möchten.", "selectDomainTypeCnameName": "Einzelne Domain (CNAME)", @@ -1242,7 +1242,7 @@ "sidebarExpand": "Erweitern", "newtUpdateAvailable": "Update verfügbar", "newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.", - "domainPickerEnterDomain": "Domain", + "domainPickerEnterDomain": "Domäne", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Geben Sie die vollständige Domäne der Ressource ein, um verfügbare Optionen zu sehen.", "domainPickerDescriptionSaas": "Geben Sie eine vollständige Domäne, Subdomäne oder einfach einen Namen ein, um verfügbare Optionen zu sehen", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdomain: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mehr anzeigen", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Region auswählen", + "regionSelectorInfo": "Das Auswählen einer Region hilft uns, eine bessere Leistung für Ihren Standort bereitzustellen. Sie müssen sich nicht in derselben Region wie Ihr Server befinden.", + "regionSelectorPlaceholder": "Wähle eine Region", + "regionSelectorComingSoon": "Kommt bald", + "billingLoadingSubscription": "Abonnement wird geladen...", + "billingFreeTier": "Kostenlose Stufe", + "billingWarningOverLimit": "Warnung: Sie haben ein oder mehrere Nutzungslimits überschritten. Ihre Webseiten werden nicht verbunden, bis Sie Ihr Abonnement ändern oder Ihren Verbrauch anpassen.", + "billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen", + "billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@fossorial.io.", + "billingDataUsage": "Datenverbrauch", + "billingOnlineTime": "Online-Zeit der Seite", + "billingUsers": "Aktive Benutzer", + "billingDomains": "Aktive Domänen", + "billingRemoteExitNodes": "Aktive selbstgehostete Nodes", + "billingNoLimitConfigured": "Kein Limit konfiguriert", + "billingEstimatedPeriod": "Geschätzter Abrechnungszeitraum", + "billingIncludedUsage": "Inklusive Nutzung", + "billingIncludedUsageDescription": "Nutzung, die in Ihrem aktuellen Abonnementplan enthalten ist", + "billingFreeTierIncludedUsage": "Nutzungskontingente der kostenlosen Stufe", + "billingIncluded": "inbegriffen", + "billingEstimatedTotal": "Geschätzte Gesamtsumme:", + "billingNotes": "Notizen", + "billingEstimateNote": "Dies ist eine Schätzung basierend auf Ihrem aktuellen Verbrauch.", + "billingActualChargesMayVary": "Tatsächliche Kosten können variieren.", + "billingBilledAtEnd": "Sie werden am Ende des Abrechnungszeitraums in Rechnung gestellt.", + "billingModifySubscription": "Abonnement ändern", + "billingStartSubscription": "Abonnement starten", + "billingRecurringCharge": "Wiederkehrende Kosten", + "billingManageSubscriptionSettings": "Verwalten Sie Ihre Abonnement-Einstellungen und Präferenzen", + "billingNoActiveSubscription": "Sie haben kein aktives Abonnement. Starten Sie Ihr Abonnement, um Nutzungslimits zu erhöhen.", + "billingFailedToLoadSubscription": "Fehler beim Laden des Abonnements", + "billingFailedToLoadUsage": "Fehler beim Laden der Nutzung", + "billingFailedToGetCheckoutUrl": "Fehler beim Abrufen der Checkout-URL", + "billingPleaseTryAgainLater": "Bitte versuchen Sie es später noch einmal.", + "billingCheckoutError": "Checkout-Fehler", + "billingFailedToGetPortalUrl": "Fehler beim Abrufen der Portal-URL", + "billingPortalError": "Portalfehler", + "billingDataUsageInfo": "Wenn Sie mit der Cloud verbunden sind, werden alle Daten über Ihre sicheren Tunnel belastet. Dies schließt eingehenden und ausgehenden Datenverkehr über alle Ihre Websites ein. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Daten werden nicht belastet, wenn Sie Knoten verwenden.", + "billingOnlineTimeInfo": "Sie werden belastet, abhängig davon, wie lange Ihre Seiten mit der Cloud verbunden bleiben. Zum Beispiel 44.640 Minuten entspricht einer Site, die 24 Stunden am Tag des Monats läuft. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Die Zeit wird nicht belastet, wenn Sie Knoten verwenden.", + "billingUsersInfo": "Ihnen wird für jeden Benutzer in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Benutzerkonten in Ihrer Organisation.", + "billingDomainInfo": "Ihnen wird für jede Domäne in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Domänenkonten in Ihrer Organisation.", + "billingRemoteExitNodesInfo": "Ihnen wird für jeden verwalteten Node in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven verwalteten Nodes in Ihrer Organisation.", "domainNotFound": "Domain nicht gefunden", "domainNotFoundDescription": "Diese Ressource ist deaktiviert, weil die Domain nicht mehr in unserem System existiert. Bitte setzen Sie eine neue Domain für diese Ressource.", "failed": "Fehlgeschlagen", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Es kann einige Zeit dauern, bis DNS-Änderungen im Internet verbreitet werden. Dies kann je nach Ihrem DNS-Provider und den TTL-Einstellungen von einigen Minuten bis zu 48 Stunden dauern.", "resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich", "resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Preisrechner", "signUpTerms": { "IAgreeToThe": "Ich stimme den", "termsOfService": "Nutzungsbedingungen zu", @@ -1371,7 +1371,7 @@ "privacyPolicy": "Datenschutzrichtlinie" }, "siteRequired": "Standort ist erforderlich.", - "olmTunnel": "Olm Tunnel", + "olmTunnel": "Olm-Tunnel", "olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung", "errorCreatingClient": "Fehler beim Erstellen des Clients", "clientDefaultsNotFound": "Kundenvorgaben nicht gefunden", @@ -1412,41 +1412,41 @@ "addNewTarget": "Neues Ziel hinzufügen", "targetsList": "Ziel-Liste", "targetErrorDuplicateTargetFound": "Doppeltes Ziel gefunden", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", + "healthCheckHealthy": "Gesund", + "healthCheckUnhealthy": "Ungesund", + "healthCheckUnknown": "Unbekannt", + "healthCheck": "Gesundheits-Check", + "configureHealthCheck": "Gesundheits-Check konfigurieren", + "configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein", + "enableHealthChecks": "Gesundheits-Checks aktivieren", + "enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.", + "healthScheme": "Methode", + "healthSelectScheme": "Methode auswählen", + "healthCheckPath": "Pfad", "healthHostname": "IP / Host", "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", + "healthCheckPathDescription": "Der Pfad zum Überprüfen des Gesundheitszustands.", + "healthyIntervalSeconds": "Gesunder Intervall", + "unhealthyIntervalSeconds": "Ungesunder Intervall", + "IntervalSeconds": "Gesunder Intervall", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "timeIsInSeconds": "Zeit ist in Sekunden", + "retryAttempts": "Wiederholungsversuche", + "expectedResponseCodes": "Erwartete Antwortcodes", + "expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.", "customHeaders": "Eigene Kopfzeilen", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Header neue Zeile getrennt: Header-Name: Wert", + "headersValidationError": "Header müssen im Format Header-Name: Wert sein.", + "saveHealthCheck": "Gesundheits-Check speichern", + "healthCheckSaved": "Gesundheits-Check gespeichert", + "healthCheckSavedDescription": "Die Konfiguration des Gesundheits-Checks wurde erfolgreich gespeichert", + "healthCheckError": "Fehler beim Gesundheits-Check", + "healthCheckErrorDescription": "Beim Speichern der Gesundheits-Check-Konfiguration ist ein Fehler aufgetreten", + "healthCheckPathRequired": "Gesundheits-Check-Pfad ist erforderlich", + "healthCheckMethodRequired": "HTTP-Methode ist erforderlich", + "healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen", + "healthCheckTimeoutMin": "Timeout muss mindestens 1 Sekunde betragen", + "healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen", "httpMethod": "HTTP-Methode", "selectHttpMethod": "HTTP-Methode auswählen", "domainPickerSubdomainLabel": "Subdomain", @@ -1460,8 +1460,8 @@ "domainPickerEnterSubdomainToSearch": "Geben Sie eine Subdomain ein, um verfügbare freie Domains zu suchen und auszuwählen.", "domainPickerFreeDomains": "Freie Domains", "domainPickerSearchForAvailableDomains": "Verfügbare Domains suchen", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", - "resourceDomain": "Domain", + "domainPickerNotWorkSelfHosted": "Hinweis: Kostenlose bereitgestellte Domains sind derzeit nicht für selbstgehostete Instanzen verfügbar.", + "resourceDomain": "Domäne", "resourceEditDomain": "Domain bearbeiten", "siteName": "Site-Name", "proxyPort": "Port", @@ -1543,72 +1543,72 @@ "autoLoginError": "Fehler bei der automatischen Anmeldung", "autoLoginErrorNoRedirectUrl": "Keine Weiterleitungs-URL vom Identitätsanbieter erhalten.", "autoLoginErrorGeneratingUrl": "Fehler beim Generieren der Authentifizierungs-URL.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Selbst-Hosted verwalten", + "remoteExitNodeDescription": "Knoten verwalten, um die Netzwerkverbindung zu erweitern", + "remoteExitNodes": "Knoten", + "searchRemoteExitNodes": "Knoten suchen...", + "remoteExitNodeAdd": "Knoten hinzufügen", + "remoteExitNodeErrorDelete": "Fehler beim Löschen des Knotens", + "remoteExitNodeQuestionRemove": "Sind Sie sicher, dass Sie den Knoten {selectedNode} aus der Organisation entfernen möchten?", + "remoteExitNodeMessageRemove": "Einmal entfernt, wird der Knoten nicht mehr zugänglich sein.", + "remoteExitNodeMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Knotens unten ein.", + "remoteExitNodeConfirmDelete": "Löschknoten bestätigen", + "remoteExitNodeDelete": "Knoten löschen", + "sidebarRemoteExitNodes": "Knoten", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Knoten erstellen", + "description": "Erstellen Sie einen neuen Knoten, um Ihre Netzwerkverbindung zu erweitern", + "viewAllButton": "Alle Knoten anzeigen", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Erstellungsstrategie", + "description": "Wählen Sie diese Option, um Ihren Knoten manuell zu konfigurieren oder neue Zugangsdaten zu generieren.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Node übernehmen", + "description": "Wählen Sie dies, wenn Sie bereits die Anmeldedaten für den Knoten haben." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Schlüssel generieren", + "description": "Wählen Sie dies, wenn Sie neue Schlüssel für den Knoten generieren möchten" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Vorhandenen Node übernehmen", + "description": "Geben Sie die Zugangsdaten des vorhandenen Knotens ein, den Sie übernehmen möchten", + "nodeIdLabel": "Knoten-ID", + "nodeIdDescription": "Die ID des vorhandenen Knotens, den Sie übernehmen möchten", + "secretLabel": "Geheimnis", + "secretDescription": "Der geheime Schlüssel des vorhandenen Knotens", + "submitButton": "Node übernehmen" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Generierte Anmeldedaten", + "description": "Verwenden Sie diese generierten Anmeldeinformationen, um Ihren Knoten zu konfigurieren", + "nodeIdTitle": "Knoten-ID", + "secretTitle": "Geheimnis", + "saveCredentialsTitle": "Anmeldedaten zur Konfiguration hinzufügen", + "saveCredentialsDescription": "Fügen Sie diese Anmeldedaten zu Ihrer selbst-gehosteten Pangolin Node-Konfigurationsdatei hinzu, um die Verbindung abzuschließen.", + "submitButton": "Knoten erstellen" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "Knoten-ID und Geheimnis sind erforderlich, wenn ein existierender Knoten angenommen wird" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Fehler beim Laden der Standardeinstellungen", + "defaultsNotLoaded": "Standardeinstellungen nicht geladen", + "createFailed": "Knoten konnte nicht erstellt werden" }, "success": { - "created": "Node created successfully" + "created": "Knoten erfolgreich erstellt" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Knotenauswahl", + "remoteExitNodeSelectionDescription": "Wählen Sie einen Knoten aus, durch den Traffic für diese lokale Seite geleitet werden soll", + "remoteExitNodeRequired": "Ein Knoten muss für lokale Seiten ausgewählt sein", + "noRemoteExitNodesAvailable": "Keine Knoten verfügbar", + "noRemoteExitNodesAvailableDescription": "Für diese Organisation sind keine Knoten verfügbar. Erstellen Sie zuerst einen Knoten, um lokale Sites zu verwenden.", + "exitNode": "Exit-Node", + "country": "Land", + "rulesMatchCountry": "Derzeit basierend auf der Quell-IP", "managedSelfHosted": { "title": "Verwaltetes Selbsthosted", "description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internationale Domain erkannt", "willbestoredas": "Wird gespeichert als:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Legen Sie fest, wie den Benutzern Rollen zugewiesen werden, wenn sie sich anmelden, wenn Auto Provision aktiviert ist.", + "selectRole": "Wählen Sie eine Rolle", + "roleMappingExpression": "Ausdruck", + "selectRolePlaceholder": "Rolle auswählen", + "selectRoleDescription": "Wählen Sie eine Rolle aus, die allen Benutzern von diesem Identitätsprovider zugewiesen werden soll", + "roleMappingExpressionDescription": "Geben Sie einen JMESPath-Ausdruck ein, um Rolleninformationen aus dem ID-Token zu extrahieren", + "idpTenantIdRequired": "Mandant ID ist erforderlich", + "invalidValue": "Ungültiger Wert", + "idpTypeLabel": "Identitätsanbietertyp", + "roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'", + "idpGoogleConfiguration": "Google-Konfiguration", + "idpGoogleConfigurationDescription": "Konfigurieren Sie Ihre Google OAuth2 Zugangsdaten", + "idpGoogleClientIdDescription": "Ihre Google OAuth2 Client-ID", + "idpGoogleClientSecretDescription": "Ihr Google OAuth2 Client Secret", + "idpAzureConfiguration": "Azure Entra ID Konfiguration", + "idpAzureConfigurationDescription": "Konfigurieren Sie Ihre Azure Entra ID OAuth2 Zugangsdaten", + "idpTenantId": "Mandanten-ID", + "idpTenantIdPlaceholder": "deine Mandant-ID", + "idpAzureTenantIdDescription": "Ihre Azure Mieter-ID (gefunden in Azure Active Directory Übersicht)", + "idpAzureClientIdDescription": "Ihre Azure App Registration Client ID", + "idpAzureClientSecretDescription": "Ihr Azure App Registration Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google-Konfiguration", + "idpAzureConfigurationTitle": "Azure Entra ID Konfiguration", + "idpTenantIdLabel": "Mandanten-ID", + "idpAzureClientIdDescription2": "Ihre Azure App Registration Client ID", + "idpAzureClientSecretDescription2": "Ihr Azure App Registration Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC Provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Subnetz", + "subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.", + "authPage": "Auth Seite", + "authPageDescription": "Konfigurieren Sie die Auth-Seite für Ihre Organisation", + "authPageDomain": "Domain der Auth Seite", + "noDomainSet": "Keine Domäne gesetzt", + "changeDomain": "Domain ändern", + "selectDomain": "Domain auswählen", + "restartCertificate": "Zertifikat neu starten", + "editAuthPageDomain": "Auth Page Domain bearbeiten", + "setAuthPageDomain": "Domain der Auth Seite festlegen", + "failedToFetchCertificate": "Zertifikat konnte nicht abgerufen werden", + "failedToRestartCertificate": "Zertifikat konnte nicht neu gestartet werden", + "addDomainToEnableCustomAuthPages": "Fügen Sie eine Domain hinzu, um benutzerdefinierte Authentifizierungsseiten für Ihre Organisation zu aktivieren", + "selectDomainForOrgAuthPage": "Wählen Sie eine Domain für die Authentifizierungsseite der Organisation", "domainPickerProvidedDomain": "Angegebene Domain", "domainPickerFreeProvidedDomain": "Kostenlose Domain", "domainPickerVerified": "Verifiziert", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" konnte nicht für {domain} gültig gemacht werden.", "domainPickerSubdomainSanitized": "Subdomain bereinigt", "domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Bei Ihrer Organisation anmelden", + "orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren", + "orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.", + "orgAuthSignInWithPangolin": "Mit Pangolin anmelden", + "subscriptionRequiredToUse": "Um diese Funktion nutzen zu können, ist ein Abonnement erforderlich.", + "idpDisabled": "Identitätsanbieter sind deaktiviert.", + "orgAuthPageDisabled": "Organisations-Authentifizierungsseite ist deaktiviert.", + "domainRestartedDescription": "Domain-Verifizierung erfolgreich neu gestartet", "resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml", "emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.", "twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Beim Aktualisieren der Auth-Seiten-Einstellungen ist ein Fehler aufgetreten", + "authPageUpdated": "Auth-Seite erfolgreich aktualisiert", + "healthCheckNotAvailable": "Lokal", + "rewritePath": "Pfad neu schreiben", + "rewritePathDescription": "Optional den Pfad umschreiben, bevor er an das Ziel weitergeleitet wird." } From 170da08001170b1ba36cd9e54fa28eff584dc22b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:58 -0700 Subject: [PATCH 146/322] New translations en-us.json (Italian) --- messages/it-IT.json | 378 ++++++++++++++++++++++---------------------- 1 file changed, 189 insertions(+), 189 deletions(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 93fa58d0..40f9811f 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -36,8 +36,8 @@ "viewSettings": "Visualizza impostazioni", "delete": "Elimina", "name": "Nome", - "online": "Online", - "offline": "Offline", + "online": "In linea", + "offline": "Non in linea", "site": "Sito", "dataIn": "Dati In", "dataOut": "Dati Fuori", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint nella rete. Nessuna configurazione aggiuntiva.", "siteWg": "WireGuard Base", "siteWgDescription": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta. FUNZIONA SOLO SU NODI AUTO-OSPITATI", "siteLocalDescription": "Solo risorse locali. Nessun tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Solo risorse locali. Nessun tunneling. FUNZIONA SOLO SU NODI AUTO-OSPITATI", "siteSeeAll": "Vedi Tutti I Siti", "siteTunnelDescription": "Determina come vuoi connetterti al tuo sito", "siteNewtCredentials": "Credenziali Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "Risorsa HTTPS", "resourceHTTPDescription": "Richieste proxy alla tua app tramite HTTPS utilizzando un sottodominio o un dominio di base.", "resourceRaw": "Risorsa Raw TCP/UDP", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Richieste proxy alla tua app tramite TCP/UDP utilizzando un numero di porta.", "resourceCreate": "Crea Risorsa", "resourceCreateDescription": "Segui i passaggi seguenti per creare una nuova risorsa", "resourceSeeAll": "Vedi Tutte Le Risorse", @@ -168,9 +168,9 @@ "siteSelect": "Seleziona sito", "siteSearch": "Cerca sito", "siteNotFound": "Nessun sito trovato.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Seleziona paese", + "searchCountries": "Cerca paesi...", + "noCountryFound": "Nessun paese trovato.", "siteSelectionDescription": "Questo sito fornirà connettività all'obiettivo.", "resourceType": "Tipo Di Risorsa", "resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa", @@ -1221,7 +1221,7 @@ "orgBillingDescription": "Gestisci le tue informazioni di fatturazione e abbonamenti", "github": "GitHub", "pangolinHosted": "Pangolin Hosted", - "fossorial": "Fossorial", + "fossorial": "Fossoriale", "completeAccountSetup": "Completa la Configurazione dell'Account", "completeAccountSetupDescription": "Imposta la tua password per iniziare", "accountSetupSent": "Invieremo un codice di configurazione dell'account a questo indirizzo email.", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Sottodominio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostra Altro", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Seleziona regione", + "regionSelectorInfo": "Selezionare una regione ci aiuta a fornire migliori performance per la tua posizione. Non devi necessariamente essere nella stessa regione del tuo server.", + "regionSelectorPlaceholder": "Scegli una regione", + "regionSelectorComingSoon": "Prossimamente", + "billingLoadingSubscription": "Caricamento abbonamento...", + "billingFreeTier": "Piano Gratuito", + "billingWarningOverLimit": "Avviso: Hai superato uno o più limiti di utilizzo. I tuoi siti non si connetteranno finché non modifichi il tuo abbonamento o non adegui il tuo utilizzo.", + "billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo", + "billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@fossorial.io.", + "billingDataUsage": "Utilizzo dei Dati", + "billingOnlineTime": "Tempo Online del Sito", + "billingUsers": "Utenti Attivi", + "billingDomains": "Domini Attivi", + "billingRemoteExitNodes": "Nodi Self-hosted Attivi", + "billingNoLimitConfigured": "Nessun limite configurato", + "billingEstimatedPeriod": "Periodo di Fatturazione Stimato", + "billingIncludedUsage": "Utilizzo Incluso", + "billingIncludedUsageDescription": "Utilizzo incluso nel tuo piano di abbonamento corrente", + "billingFreeTierIncludedUsage": "Elenchi di utilizzi inclusi nel piano gratuito", + "billingIncluded": "incluso", + "billingEstimatedTotal": "Totale Stimato:", + "billingNotes": "Note", + "billingEstimateNote": "Questa è una stima basata sul tuo utilizzo attuale.", + "billingActualChargesMayVary": "I costi effettivi possono variare.", + "billingBilledAtEnd": "Sarai fatturato alla fine del periodo di fatturazione.", + "billingModifySubscription": "Modifica Abbonamento", + "billingStartSubscription": "Inizia Abbonamento", + "billingRecurringCharge": "Addebito Ricorrente", + "billingManageSubscriptionSettings": "Gestisci impostazioni e preferenze dell'abbonamento", + "billingNoActiveSubscription": "Non hai un abbonamento attivo. Avvia il tuo abbonamento per aumentare i limiti di utilizzo.", + "billingFailedToLoadSubscription": "Caricamento abbonamento fallito", + "billingFailedToLoadUsage": "Caricamento utilizzo fallito", + "billingFailedToGetCheckoutUrl": "Errore durante l'ottenimento dell'URL di pagamento", + "billingPleaseTryAgainLater": "Per favore, riprova più tardi.", + "billingCheckoutError": "Errore di Pagamento", + "billingFailedToGetPortalUrl": "Errore durante l'ottenimento dell'URL del portale", + "billingPortalError": "Errore del Portale", + "billingDataUsageInfo": "Hai addebitato tutti i dati trasferiti attraverso i tunnel sicuri quando sei connesso al cloud. Questo include sia il traffico in entrata e in uscita attraverso tutti i siti. Quando si raggiunge il limite, i siti si disconnetteranno fino a quando non si aggiorna il piano o si riduce l'utilizzo. I dati non vengono caricati quando si utilizzano nodi.", + "billingOnlineTimeInfo": "Ti viene addebitato in base al tempo in cui i tuoi siti rimangono connessi al cloud. Ad esempio, 44,640 minuti è uguale a un sito in esecuzione 24/7 per un mese intero. Quando raggiungi il tuo limite, i tuoi siti si disconnetteranno fino a quando non aggiorni il tuo piano o riduci l'utilizzo. Il tempo non viene caricato quando si usano i nodi.", + "billingUsersInfo": "Sei addebitato per ogni utente nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account utente attivi nella tua organizzazione.", + "billingDomainInfo": "Sei addebitato per ogni dominio nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account dominio attivi nella tua organizzazione.", + "billingRemoteExitNodesInfo": "Sei addebitato per ogni nodo gestito nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di nodi gestiti attivi nella tua organizzazione.", "domainNotFound": "Domini Non Trovati", "domainNotFoundDescription": "Questa risorsa è disabilitata perché il dominio non esiste più nel nostro sistema. Si prega di impostare un nuovo dominio per questa risorsa.", "failed": "Fallito", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Le modifiche DNS possono richiedere del tempo per propagarsi in Internet. Questo può richiedere da pochi minuti a 48 ore, a seconda del tuo provider DNS e delle impostazioni TTL.", "resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP", "resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Calcolatore di Prezzi", "signUpTerms": { "IAgreeToThe": "Accetto i", "termsOfService": "termini di servizio", @@ -1371,7 +1371,7 @@ "privacyPolicy": "informativa sulla privacy" }, "siteRequired": "Il sito è richiesto.", - "olmTunnel": "Olm Tunnel", + "olmTunnel": "Tunnel Olm", "olmTunnelDescription": "Usa Olm per la connettività client", "errorCreatingClient": "Errore nella creazione del client", "clientDefaultsNotFound": "Impostazioni predefinite del client non trovate", @@ -1412,41 +1412,41 @@ "addNewTarget": "Aggiungi Nuovo Target", "targetsList": "Elenco dei Target", "targetErrorDuplicateTargetFound": "Target duplicato trovato", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", + "healthCheckHealthy": "Sano", + "healthCheckUnhealthy": "Non Sano", + "healthCheckUnknown": "Sconosciuto", + "healthCheck": "Controllo Salute", + "configureHealthCheck": "Configura Controllo Salute", + "configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}", + "enableHealthChecks": "Abilita i Controlli di Salute", + "enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.", + "healthScheme": "Metodo", + "healthSelectScheme": "Seleziona Metodo", + "healthCheckPath": "Percorso", + "healthHostname": "IP / Nome host", + "healthPort": "Porta", + "healthCheckPathDescription": "Percorso per verificare lo stato di salute.", + "healthyIntervalSeconds": "Intervallo Sano", + "unhealthyIntervalSeconds": "Intervallo Non Sano", + "IntervalSeconds": "Intervallo Sano", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "timeIsInSeconds": "Il tempo è in secondi", + "retryAttempts": "Tentativi di Riprova", + "expectedResponseCodes": "Codici di Risposta Attesi", + "expectedResponseCodesDescription": "Codice di stato HTTP che indica lo stato di salute. Se lasciato vuoto, considerato sano è compreso tra 200-300.", "customHeaders": "Intestazioni Personalizzate", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Intestazioni nuova riga separate: Intestazione-Nome: valore", + "headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.", + "saveHealthCheck": "Salva Controllo Salute", + "healthCheckSaved": "Controllo Salute Salvato", + "healthCheckSavedDescription": "La configurazione del controllo salute è stata salvata con successo", + "healthCheckError": "Errore Controllo Salute", + "healthCheckErrorDescription": "Si è verificato un errore durante il salvataggio della configurazione del controllo salute.", + "healthCheckPathRequired": "Il percorso del controllo salute è richiesto", + "healthCheckMethodRequired": "Metodo HTTP richiesto", + "healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi", + "healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo", + "healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1", "httpMethod": "Metodo HTTP", "selectHttpMethod": "Seleziona metodo HTTP", "domainPickerSubdomainLabel": "Sottodominio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Inserisci un sottodominio per cercare e selezionare dai domini gratuiti disponibili.", "domainPickerFreeDomains": "Domini Gratuiti", "domainPickerSearchForAvailableDomains": "Cerca domini disponibili", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Nota: I domini forniti gratuitamente non sono disponibili per le istanze self-hosted al momento.", "resourceDomain": "Dominio", "resourceEditDomain": "Modifica Dominio", "siteName": "Nome del Sito", @@ -1543,72 +1543,72 @@ "autoLoginError": "Errore di Accesso Automatico", "autoLoginErrorNoRedirectUrl": "Nessun URL di reindirizzamento ricevuto dal provider di identità.", "autoLoginErrorGeneratingUrl": "Impossibile generare l'URL di autenticazione.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Gestisci Self-Hosted", + "remoteExitNodeDescription": "Gestisci i nodi per estendere la connettività di rete", + "remoteExitNodes": "Nodi", + "searchRemoteExitNodes": "Cerca nodi...", + "remoteExitNodeAdd": "Aggiungi Nodo", + "remoteExitNodeErrorDelete": "Errore nell'eliminare il nodo", + "remoteExitNodeQuestionRemove": "Sei sicuro di voler rimuovere il nodo {selectedNode} dall'organizzazione?", + "remoteExitNodeMessageRemove": "Una volta rimosso, il nodo non sarà più accessibile.", + "remoteExitNodeMessageConfirm": "Per confermare, digita il nome del nodo qui sotto.", + "remoteExitNodeConfirmDelete": "Conferma Eliminazione Nodo", + "remoteExitNodeDelete": "Elimina Nodo", + "sidebarRemoteExitNodes": "Nodi", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Crea Nodo", + "description": "Crea un nuovo nodo per estendere la connettività di rete", + "viewAllButton": "Visualizza Tutti I Nodi", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Strategia di Creazione", + "description": "Scegli questa opzione per configurare manualmente il nodo o generare nuove credenziali.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adotta Nodo", + "description": "Scegli questo se hai già le credenziali per il nodo." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Genera Chiavi", + "description": "Scegli questa opzione se vuoi generare nuove chiavi per il nodo" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Adotta Nodo Esistente", + "description": "Inserisci le credenziali del nodo esistente che vuoi adottare", + "nodeIdLabel": "ID Nodo", + "nodeIdDescription": "L'ID del nodo esistente che si desidera adottare", + "secretLabel": "Segreto", + "secretDescription": "La chiave segreta del nodo esistente", + "submitButton": "Adotta Nodo" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Credenziali Generate", + "description": "Usa queste credenziali generate per configurare il nodo", + "nodeIdTitle": "ID Nodo", + "secretTitle": "Segreto", + "saveCredentialsTitle": "Aggiungi Credenziali alla Configurazione", + "saveCredentialsDescription": "Aggiungi queste credenziali al tuo file di configurazione del nodo self-hosted Pangolin per completare la connessione.", + "submitButton": "Crea Nodo" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "L'ID del nodo e il segreto sono necessari quando si adotta un nodo esistente" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Caricamento impostazioni predefinite fallito", + "defaultsNotLoaded": "Impostazioni predefinite non caricate", + "createFailed": "Impossibile creare il nodo" }, "success": { - "created": "Node created successfully" + "created": "Nodo creato con successo" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Selezione Nodo", + "remoteExitNodeSelectionDescription": "Seleziona un nodo per instradare il traffico per questo sito locale", + "remoteExitNodeRequired": "Un nodo deve essere selezionato per i siti locali", + "noRemoteExitNodesAvailable": "Nessun Nodo Disponibile", + "noRemoteExitNodesAvailableDescription": "Non ci sono nodi disponibili per questa organizzazione. Crea un nodo prima per usare i siti locali.", + "exitNode": "Nodo di Uscita", + "country": "Paese", + "rulesMatchCountry": "Attualmente basato sull'IP di origine", "managedSelfHosted": { "title": "Gestito Auto-Ospitato", "description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internazionale Rilevato", "willbestoredas": "Verrà conservato come:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Determinare come i ruoli sono assegnati agli utenti quando accedono quando è abilitata la fornitura automatica.", + "selectRole": "Seleziona un ruolo", + "roleMappingExpression": "Espressione", + "selectRolePlaceholder": "Scegli un ruolo", + "selectRoleDescription": "Seleziona un ruolo da assegnare a tutti gli utenti da questo provider di identità", + "roleMappingExpressionDescription": "Inserire un'espressione JMESPath per estrarre le informazioni sul ruolo dal token ID", + "idpTenantIdRequired": "L'ID dell'inquilino è obbligatorio", + "invalidValue": "Valore non valido", + "idpTypeLabel": "Tipo Provider Identità", + "roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' 'Membro'", + "idpGoogleConfiguration": "Configurazione Google", + "idpGoogleConfigurationDescription": "Configura le tue credenziali di Google OAuth2", + "idpGoogleClientIdDescription": "Il Tuo Client Id Google OAuth2", + "idpGoogleClientSecretDescription": "Il Tuo Client Google OAuth2 Secret", + "idpAzureConfiguration": "Configurazione Azure Entra ID", + "idpAzureConfigurationDescription": "Configura le credenziali OAuth2 di Azure Entra ID", + "idpTenantId": "ID Tenant", + "idpTenantIdPlaceholder": "iltuo-inquilino-id", + "idpAzureTenantIdDescription": "Il tuo ID del tenant Azure (trovato nella panoramica di Azure Active Directory)", + "idpAzureClientIdDescription": "Il Tuo Id Client Registrazione App Azure", + "idpAzureClientSecretDescription": "Il Tuo Client Di Registrazione App Azure Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Configurazione Google", + "idpAzureConfigurationTitle": "Configurazione Azure Entra ID", + "idpTenantIdLabel": "ID Tenant", + "idpAzureClientIdDescription2": "Il Tuo Id Client Registrazione App Azure", + "idpAzureClientSecretDescription2": "Il Tuo Client Di Registrazione App Azure Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Sottorete", + "subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.", + "authPage": "Pagina Autenticazione", + "authPageDescription": "Configura la pagina di autenticazione per la tua organizzazione", + "authPageDomain": "Dominio Pagina Auth", + "noDomainSet": "Nessun dominio impostato", + "changeDomain": "Cambia Dominio", + "selectDomain": "Seleziona Dominio", + "restartCertificate": "Riavvia Certificato", + "editAuthPageDomain": "Modifica Dominio Pagina Auth", + "setAuthPageDomain": "Imposta Dominio Pagina Autenticazione", + "failedToFetchCertificate": "Recupero del certificato non riuscito", + "failedToRestartCertificate": "Riavvio del certificato non riuscito", + "addDomainToEnableCustomAuthPages": "Aggiungi un dominio per abilitare le pagine di autenticazione personalizzate per la tua organizzazione", + "selectDomainForOrgAuthPage": "Seleziona un dominio per la pagina di autenticazione dell'organizzazione", "domainPickerProvidedDomain": "Dominio Fornito", "domainPickerFreeProvidedDomain": "Dominio Fornito Gratuito", "domainPickerVerified": "Verificato", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" non può essere reso valido per {domain}.", "domainPickerSubdomainSanitized": "Sottodominio igienizzato", "domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Accedi alla tua organizzazione", + "orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare", + "orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.", + "orgAuthSignInWithPangolin": "Accedi con Pangolino", + "subscriptionRequiredToUse": "Per utilizzare questa funzionalità è necessario un abbonamento.", + "idpDisabled": "I provider di identità sono disabilitati.", + "orgAuthPageDisabled": "La pagina di autenticazione dell'organizzazione è disabilitata.", + "domainRestartedDescription": "Verifica del dominio riavviata con successo", "resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Modifica file: docker-compose.yml", "emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", "twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Si è verificato un errore durante l'aggiornamento delle impostazioni della pagina di autenticazione", + "authPageUpdated": "Pagina di autenticazione aggiornata con successo", + "healthCheckNotAvailable": "Locale", + "rewritePath": "Riscrivi percorso", + "rewritePathDescription": "Riscrivi eventualmente il percorso prima di inoltrarlo al target." } From 4853c8c87248f8506b69b46b89c06420f2dacd27 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:16:59 -0700 Subject: [PATCH 147/322] New translations en-us.json (Korean) --- messages/ko-KR.json | 372 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 34888669..3d010cd5 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "네트워크에 대한 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.", "siteWg": "기본 WireGuard", "siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다. 자체 호스팅 노드에서만 작동합니다.", "siteLocalDescription": "로컬 리소스만 사용 가능합니다. 터널링이 없습니다.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "로컬 리소스만. 터널링 없음. 자체 호스팅 노드에서만 작동합니다.", "siteSeeAll": "모든 사이트 보기", "siteTunnelDescription": "사이트에 연결하는 방법을 결정하세요", "siteNewtCredentials": "Newt 자격 증명", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS 리소스", "resourceHTTPDescription": "서브도메인 또는 기본 도메인을 사용하여 HTTPS를 통해 앱에 대한 요청을 프록시합니다.", "resourceRaw": "원시 TCP/UDP 리소스", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "TCP/UDP를 통해 포트 번호를 사용하여 앱에 요청을 프록시합니다.", "resourceCreate": "리소스 생성", "resourceCreateDescription": "아래 단계를 따라 새 리소스를 생성하세요.", "resourceSeeAll": "모든 리소스 보기", @@ -168,9 +168,9 @@ "siteSelect": "사이트 선택", "siteSearch": "사이트 검색", "siteNotFound": "사이트를 찾을 수 없습니다.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "국가 선택하기", + "searchCountries": "국가 검색...", + "noCountryFound": "국가를 찾을 수 없습니다.", "siteSelectionDescription": "이 사이트는 대상에 대한 연결을 제공합니다.", "resourceType": "리소스 유형", "resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "서브도메인: {subdomain}", "domainPickerNamespace": "이름 공간: {namespace}", "domainPickerShowMore": "더보기", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "지역 선택", + "regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.", + "regionSelectorPlaceholder": "지역 선택", + "regionSelectorComingSoon": "곧 출시 예정", + "billingLoadingSubscription": "구독 불러오는 중...", + "billingFreeTier": "무료 티어", + "billingWarningOverLimit": "경고: 하나 이상의 사용 한도를 초과했습니다. 구독을 수정하거나 사용량을 조정하기 전까지 사이트는 연결되지 않습니다.", + "billingUsageLimitsOverview": "사용 한도 개요", + "billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@fossorial.io로 연락하십시오.", + "billingDataUsage": "데이터 사용량", + "billingOnlineTime": "사이트 온라인 시간", + "billingUsers": "활성 사용자", + "billingDomains": "활성 도메인", + "billingRemoteExitNodes": "활성 자체 호스팅 노드", + "billingNoLimitConfigured": "구성된 한도가 없습니다.", + "billingEstimatedPeriod": "예상 청구 기간", + "billingIncludedUsage": "포함 사용량", + "billingIncludedUsageDescription": "현재 구독 계획에 포함된 사용량", + "billingFreeTierIncludedUsage": "무료 티어 사용 허용량", + "billingIncluded": "포함됨", + "billingEstimatedTotal": "예상 총액:", + "billingNotes": "노트", + "billingEstimateNote": "현재 사용량을 기반으로 한 추정치입니다.", + "billingActualChargesMayVary": "실제 청구 금액은 다를 수 있습니다.", + "billingBilledAtEnd": "청구 기간이 끝난 후 청구됩니다.", + "billingModifySubscription": "구독 수정", + "billingStartSubscription": "구독 시작", + "billingRecurringCharge": "반복 요금", + "billingManageSubscriptionSettings": "구독 설정 및 기본 설정을 관리합니다", + "billingNoActiveSubscription": "활성 구독이 없습니다. 사용 한도를 늘리려면 구독을 시작하십시오.", + "billingFailedToLoadSubscription": "구독을 불러오는 데 실패했습니다.", + "billingFailedToLoadUsage": "사용량을 불러오는 데 실패했습니다.", + "billingFailedToGetCheckoutUrl": "체크아웃 URL을 가져오는 데 실패했습니다.", + "billingPleaseTryAgainLater": "나중에 다시 시도하십시오.", + "billingCheckoutError": "체크아웃 오류", + "billingFailedToGetPortalUrl": "포털 URL을 가져오는 데 실패했습니다.", + "billingPortalError": "포털 오류", + "billingDataUsageInfo": "클라우드에 연결할 때 보안 터널을 통해 전송된 모든 데이터에 대해 비용이 청구됩니다. 여기에는 모든 사이트의 들어오고 나가는 트래픽이 포함됩니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용하는 경우 데이터는 요금이 청구되지 않습니다.", + "billingOnlineTimeInfo": "사이트가 클라우드에 연결된 시간에 따라 요금이 청구됩니다. 예를 들어, 44,640분은 사이트가 한 달 내내 24시간 작동하는 것과 같습니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용할 때 시간은 요금이 청구되지 않습니다.", + "billingUsersInfo": "조직의 사용자마다 요금이 청구됩니다. 청구는 조직의 활성 사용자 계정 수에 따라 매일 계산됩니다.", + "billingDomainInfo": "조직의 도메인마다 요금이 청구됩니다. 청구는 조직의 활성 도메인 계정 수에 따라 매일 계산됩니다.", + "billingRemoteExitNodesInfo": "조직의 관리 노드마다 요금이 청구됩니다. 청구는 조직의 활성 관리 노드 수에 따라 매일 계산됩니다.", "domainNotFound": "도메인을 찾을 수 없습니다", "domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.", "failed": "실패", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 변경 사항은 인터넷 전체에 전파되는 데 시간이 걸립니다. DNS 제공자와 TTL 설정에 따라 몇 분에서 48시간까지 걸릴 수 있습니다.", "resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다", "resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "가격 계산기", "signUpTerms": { "IAgreeToThe": "동의합니다", "termsOfService": "서비스 약관", @@ -1412,41 +1412,41 @@ "addNewTarget": "새 대상 추가", "targetsList": "대상 목록", "targetErrorDuplicateTargetFound": "중복 대상 발견", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "정상", + "healthCheckUnhealthy": "비정상", + "healthCheckUnknown": "알 수 없음", + "healthCheck": "상태 확인", + "configureHealthCheck": "상태 확인 설정", + "configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정", + "enableHealthChecks": "상태 확인 활성화", + "enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.", + "healthScheme": "방법", + "healthSelectScheme": "방법 선택", + "healthCheckPath": "경로", + "healthHostname": "IP / 호스트", + "healthPort": "포트", + "healthCheckPathDescription": "상태 확인을 위한 경로입니다.", + "healthyIntervalSeconds": "정상 간격", + "unhealthyIntervalSeconds": "비정상 간격", + "IntervalSeconds": "정상 간격", + "timeoutSeconds": "시간 초과", + "timeIsInSeconds": "시간은 초 단위입니다", + "retryAttempts": "재시도 횟수", + "expectedResponseCodes": "예상 응답 코드", + "expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.", "customHeaders": "사용자 정의 헤더", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "헤더는 새 줄로 구분됨: Header-Name: value", + "headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.", + "saveHealthCheck": "상태 확인 저장", + "healthCheckSaved": "상태 확인이 저장되었습니다.", + "healthCheckSavedDescription": "상태 확인 구성이 성공적으로 저장되었습니다", + "healthCheckError": "상태 확인 오류", + "healthCheckErrorDescription": "상태 확인 구성을 저장하는 동안 오류가 발생했습니다", + "healthCheckPathRequired": "상태 확인 경로는 필수입니다.", + "healthCheckMethodRequired": "HTTP 방법은 필수입니다.", + "healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.", + "healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.", + "healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.", "httpMethod": "HTTP 메소드", "selectHttpMethod": "HTTP 메소드 선택", "domainPickerSubdomainLabel": "서브도메인", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "사용 가능한 무료 도메인에서 검색 및 선택할 서브도메인 입력.", "domainPickerFreeDomains": "무료 도메인", "domainPickerSearchForAvailableDomains": "사용 가능한 도메인 검색", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "참고: 무료 제공 도메인은 현재 자체 호스팅 인스턴스에 사용할 수 없습니다.", "resourceDomain": "도메인", "resourceEditDomain": "도메인 수정", "siteName": "사이트 이름", @@ -1543,72 +1543,72 @@ "autoLoginError": "자동 로그인 오류", "autoLoginErrorNoRedirectUrl": "ID 공급자로부터 리디렉션 URL을 받지 못했습니다.", "autoLoginErrorGeneratingUrl": "인증 URL 생성 실패.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "관리 자체 호스팅", + "remoteExitNodeDescription": "네트워크 연결성을 확장하기 위해 노드를 관리하세요", + "remoteExitNodes": "노드", + "searchRemoteExitNodes": "노드 검색...", + "remoteExitNodeAdd": "노드 추가", + "remoteExitNodeErrorDelete": "노드 삭제 오류", + "remoteExitNodeQuestionRemove": "조직에서 노드 {selectedNode}를 제거하시겠습니까?", + "remoteExitNodeMessageRemove": "한 번 제거되면 더 이상 노드에 접근할 수 없습니다.", + "remoteExitNodeMessageConfirm": "확인을 위해 아래에 노드 이름을 입력해 주세요.", + "remoteExitNodeConfirmDelete": "노드 삭제 확인", + "remoteExitNodeDelete": "노드 삭제", + "sidebarRemoteExitNodes": "노드", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "노드 생성", + "description": "네트워크 연결성을 확장하기 위해 새 노드를 생성하세요", + "viewAllButton": "모든 노드 보기", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "생성 전략", + "description": "노드를 직접 구성하거나 새 자격 증명을 생성하려면 이것을 선택하세요.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "노드 채택", + "description": "이미 노드의 자격 증명이 있는 경우 이것을 선택하세요." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "키 생성", + "description": "노드에 대한 새 키를 생성하려면 이것을 선택하세요" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "기존 노드 채택", + "description": "채택하려는 기존 노드의 자격 증명을 입력하세요", + "nodeIdLabel": "노드 ID", + "nodeIdDescription": "채택하려는 기존 노드의 ID", + "secretLabel": "비밀", + "secretDescription": "기존 노드의 비밀 키", + "submitButton": "노드 채택" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "생성된 자격 증명", + "description": "생성된 자격 증명을 사용하여 노드를 구성하세요", + "nodeIdTitle": "노드 ID", + "secretTitle": "비밀", + "saveCredentialsTitle": "구성에 자격 증명 추가", + "saveCredentialsDescription": "연결을 완료하려면 이러한 자격 증명을 자체 호스팅 Pangolin 노드 구성 파일에 추가하십시오.", + "submitButton": "노드 생성" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "기존 노드를 채택하려면 노드 ID와 비밀 키가 필요합니다" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "기본값 로드 실패", + "defaultsNotLoaded": "기본값 로드되지 않음", + "createFailed": "노드 생성 실패" }, "success": { - "created": "Node created successfully" + "created": "노드가 성공적으로 생성되었습니다" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "노드 선택", + "remoteExitNodeSelectionDescription": "이 로컬 사이트에서 트래픽을 라우팅할 노드를 선택하세요", + "remoteExitNodeRequired": "로컬 사이트에 노드를 선택해야 합니다", + "noRemoteExitNodesAvailable": "사용 가능한 노드가 없습니다", + "noRemoteExitNodesAvailableDescription": "이 조직에 사용 가능한 노드가 없습니다. 로컬 사이트를 사용하려면 먼저 노드를 생성하세요.", + "exitNode": "종단 노드", + "country": "국가", + "rulesMatchCountry": "현재 소스 IP를 기반으로 합니다", "managedSelfHosted": { "title": "관리 자체 호스팅", "description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "국제 도메인 감지됨", "willbestoredas": "다음으로 저장됩니다:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", + "roleMappingDescription": "자동 프로비저닝이 활성화되면 사용자가 로그인할 때 역할이 할당되는 방법을 결정합니다.", + "selectRole": "역할 선택", + "roleMappingExpression": "표현식", + "selectRolePlaceholder": "역할 선택", + "selectRoleDescription": "이 신원 공급자로부터 모든 사용자에게 할당할 역할을 선택하십시오.", + "roleMappingExpressionDescription": "ID 토큰에서 역할 정보를 추출하기 위한 JMESPath 표현식을 입력하세요.", + "idpTenantIdRequired": "테넌트 ID가 필요합니다", + "invalidValue": "잘못된 값", + "idpTypeLabel": "신원 공급자 유형", + "roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Google 구성", + "idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.", + "idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID", + "idpGoogleClientSecretDescription": "Google OAuth2 클라이언트 비밀", + "idpAzureConfiguration": "Azure Entra ID 구성", + "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 자격 증명을 구성합니다.", + "idpTenantId": "테넌트 ID", "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", - "idpGoogleTitle": "Google", + "idpAzureTenantIdDescription": "Azure 액티브 디렉터리 개요에서 찾은 Azure 테넌트 ID", + "idpAzureClientIdDescription": "Azure 앱 등록 클라이언트 ID", + "idpAzureClientSecretDescription": "Azure 앱 등록 클라이언트 비밀", + "idpGoogleTitle": "구글", "idpGoogleAlt": "구글", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "애저", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google 구성", + "idpAzureConfigurationTitle": "Azure Entra ID 구성", + "idpTenantIdLabel": "테넌트 ID", + "idpAzureClientIdDescription2": "Azure 앱 등록 클라이언트 ID", + "idpAzureClientSecretDescription2": "Azure 앱 등록 클라이언트 비밀", "idpGoogleDescription": "Google OAuth2/OIDC 공급자", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "서브넷", + "subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.", + "authPage": "인증 페이지", + "authPageDescription": "조직에 대한 인증 페이지를 구성합니다.", + "authPageDomain": "인증 페이지 도메인", + "noDomainSet": "도메인 설정 없음", + "changeDomain": "도메인 변경", + "selectDomain": "도메인 선택", + "restartCertificate": "인증서 재시작", + "editAuthPageDomain": "인증 페이지 도메인 편집", + "setAuthPageDomain": "인증 페이지 도메인 설정", + "failedToFetchCertificate": "인증서 가져오기 실패", + "failedToRestartCertificate": "인증서 재시작 실패", + "addDomainToEnableCustomAuthPages": "조직의 맞춤 인증 페이지를 활성화하려면 도메인을 추가하세요.", + "selectDomainForOrgAuthPage": "조직 인증 페이지에 대한 도메인을 선택하세요.", "domainPickerProvidedDomain": "제공된 도메인", "domainPickerFreeProvidedDomain": "무료 제공된 도메인", "domainPickerVerified": "검증됨", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\"을(를) {domain}에 대해 유효하게 만들 수 없습니다.", "domainPickerSubdomainSanitized": "하위 도메인 정리됨", "domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "조직에 로그인", + "orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.", + "orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.", + "orgAuthSignInWithPangolin": "Pangolin으로 로그인", + "subscriptionRequiredToUse": "이 기능을 사용하려면 구독이 필요합니다.", + "idpDisabled": "신원 공급자가 비활성화되었습니다.", + "orgAuthPageDisabled": "조직 인증 페이지가 비활성화되었습니다.", + "domainRestartedDescription": "도메인 인증이 성공적으로 재시작되었습니다.", "resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "파일 편집: docker-compose.yml", "emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", "twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "인증 페이지 설정을 업데이트하는 동안 오류가 발생했습니다", + "authPageUpdated": "인증 페이지가 성공적으로 업데이트되었습니다", + "healthCheckNotAvailable": "로컬", + "rewritePath": "경로 재작성", + "rewritePathDescription": "대상으로 전달하기 전에 경로를 선택적으로 재작성합니다." } From 4c995f786b0e968c8462b981f8d17675a80316a2 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:00 -0700 Subject: [PATCH 148/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 372 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index c8047e5f..fb370345 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.", "siteWg": "Basis WireGuard", "siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Gebruik elke WireGuard-client om een tunnel op te zetten. Handmatige NAT-instelling vereist. WERKT ALLEEN OP SELF HOSTED NODES", "siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Alleen lokale bronnen. Geen tunneling. WERKT ALLEEN OP SELF HOSTED NODES", "siteSeeAll": "Alle sites bekijken", "siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site", "siteNewtCredentials": "Nieuwste aanmeldgegevens", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS bron", "resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.", "resourceRaw": "TCP/UDP bron", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.", "resourceCreate": "Bron maken", "resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken", "resourceSeeAll": "Alle bronnen bekijken", @@ -168,9 +168,9 @@ "siteSelect": "Selecteer site", "siteSearch": "Zoek site", "siteNotFound": "Geen site gevonden.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Selecteer land", + "searchCountries": "Zoek landen...", + "noCountryFound": "Geen land gevonden.", "siteSelectionDescription": "Deze site zal connectiviteit met het doelwit bieden.", "resourceType": "Type bron", "resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron", @@ -268,7 +268,7 @@ "apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen", "apiKeysList": "Uw API-sleutel", "apiKeysSave": "Uw API-sleutel opslaan", - "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.", + "apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.", "apiKeysInfo": "Uw API-sleutel is:", "apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd", "generate": "Genereren", @@ -925,7 +925,7 @@ "inviteErrorExpired": "De uitnodiging is mogelijk verlopen", "inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken", "inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink", - "pangolinSetup": "Setup - Pangolin", + "pangolinSetup": "Instellen - Pangolin", "orgNameRequired": "Organisatienaam is vereist", "orgIdRequired": "Organisatie-ID is vereist", "orgErrorCreate": "Fout opgetreden tijdens het aanmaken org", @@ -995,12 +995,12 @@ "actionGetUser": "Gebruiker ophalen", "actionGetOrgUser": "Krijg organisatie-gebruiker", "actionListOrgDomains": "Lijst organisatie domeinen", - "actionCreateSite": "Site maken", + "actionCreateSite": "Site aanmaken", "actionDeleteSite": "Site verwijderen", "actionGetSite": "Site ophalen", "actionListSites": "Sites weergeven", "actionApplyBlueprint": "Blauwdruk toepassen", - "setupToken": "Setup Token", + "setupToken": "Instel Token", "setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.", "setupTokenRequired": "Setup-token is vereist", "actionUpdateSite": "Site bijwerken", @@ -1256,50 +1256,50 @@ "domainPickerOrganizationDomains": "Organisatiedomeinen", "domainPickerProvidedDomains": "Aangeboden domeinen", "domainPickerSubdomain": "Subdomein: {subdomain}", - "domainPickerNamespace": "Namespace: {namespace}", + "domainPickerNamespace": "Naamruimte: {namespace}", "domainPickerShowMore": "Meer weergeven", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Selecteer Regio", + "regionSelectorInfo": "Het selecteren van een regio helpt ons om betere prestaties te leveren voor uw locatie. U hoeft niet in dezelfde regio als uw server te zijn.", + "regionSelectorPlaceholder": "Kies een regio", + "regionSelectorComingSoon": "Komt binnenkort", + "billingLoadingSubscription": "Abonnement laden...", + "billingFreeTier": "Gratis Niveau", + "billingWarningOverLimit": "Waarschuwing: U hebt een of meer gebruikslimieten overschreden. Uw sites maken geen verbinding totdat u uw abonnement aanpast of uw gebruik aanpast.", + "billingUsageLimitsOverview": "Overzicht gebruikslimieten", + "billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@fossorial.io.", + "billingDataUsage": "Gegevensgebruik", + "billingOnlineTime": "Site Online Tijd", + "billingUsers": "Actieve Gebruikers", + "billingDomains": "Actieve Domeinen", + "billingRemoteExitNodes": "Actieve Zelfgehoste Nodes", + "billingNoLimitConfigured": "Geen limiet ingesteld", + "billingEstimatedPeriod": "Geschatte Facturatie Periode", + "billingIncludedUsage": "Opgenomen Gebruik", + "billingIncludedUsageDescription": "Gebruik inbegrepen in uw huidige abonnementsplan", + "billingFreeTierIncludedUsage": "Gratis niveau gebruikstoelagen", + "billingIncluded": "inbegrepen", + "billingEstimatedTotal": "Geschat Totaal:", + "billingNotes": "Notities", + "billingEstimateNote": "Dit is een schatting gebaseerd op uw huidige gebruik.", + "billingActualChargesMayVary": "Facturering kan variëren.", + "billingBilledAtEnd": "U wordt aan het einde van de factureringsperiode gefactureerd.", + "billingModifySubscription": "Abonnementsaanpassing", + "billingStartSubscription": "Abonnement Starten", + "billingRecurringCharge": "Terugkerende Kosten", + "billingManageSubscriptionSettings": "Beheer uw abonnementsinstellingen en voorkeuren", + "billingNoActiveSubscription": "U heeft geen actief abonnement. Start uw abonnement om gebruikslimieten te verhogen.", + "billingFailedToLoadSubscription": "Fout bij laden van abonnement", + "billingFailedToLoadUsage": "Niet gelukt om gebruik te laden", + "billingFailedToGetCheckoutUrl": "Niet gelukt om checkout URL te krijgen", + "billingPleaseTryAgainLater": "Probeer het later opnieuw.", + "billingCheckoutError": "Checkout Fout", + "billingFailedToGetPortalUrl": "Niet gelukt om portal URL te krijgen", + "billingPortalError": "Portal Fout", + "billingDataUsageInfo": "U bent in rekening gebracht voor alle gegevens die via uw beveiligde tunnels via de cloud worden verzonden. Dit omvat zowel inkomende als uitgaande verkeer over al uw sites. Wanneer u uw limiet bereikt zullen uw sites de verbinding verbreken totdat u uw abonnement upgradet of het gebruik vermindert. Gegevens worden niet in rekening gebracht bij het gebruik van knooppunten.", + "billingOnlineTimeInfo": "U wordt in rekening gebracht op basis van hoe lang uw sites verbonden blijven met de cloud. Bijvoorbeeld 44,640 minuten is gelijk aan één site met 24/7 voor een volledige maand. Wanneer u uw limiet bereikt, zal de verbinding tussen uw sites worden verbroken totdat u een upgrade van uw abonnement uitvoert of het gebruik vermindert. Tijd wordt niet belast bij het gebruik van knooppunten.", + "billingUsersInfo": "U wordt gefactureerd voor elke gebruiker in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve gebruikersaccounts in uw organisatie.", + "billingDomainInfo": "U wordt gefactureerd voor elk domein in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve domeinaccounts in uw organisatie.", + "billingRemoteExitNodesInfo": "U wordt gefactureerd voor elke beheerde Node in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve beheerde Nodes in uw organisatie.", "domainNotFound": "Domein niet gevonden", "domainNotFoundDescription": "Deze bron is uitgeschakeld omdat het domein niet langer in ons systeem bestaat. Stel een nieuw domein in voor deze bron.", "failed": "Mislukt", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-wijzigingen kunnen enige tijd duren om over het internet te worden verspreid. Dit kan enkele minuten tot 48 uur duren, afhankelijk van je DNS-provider en TTL-instellingen.", "resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen", "resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Prijs Calculator", "signUpTerms": { "IAgreeToThe": "Ik ga akkoord met de", "termsOfService": "servicevoorwaarden", @@ -1412,41 +1412,41 @@ "addNewTarget": "Voeg nieuw doelwit toe", "targetsList": "Lijst met doelen", "targetErrorDuplicateTargetFound": "Dubbel doelwit gevonden", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", + "healthCheckHealthy": "Gezond", + "healthCheckUnhealthy": "Ongezond", + "healthCheckUnknown": "Onbekend", + "healthCheck": "Gezondheidscontrole", + "configureHealthCheck": "Configureer Gezondheidscontrole", + "configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in", + "enableHealthChecks": "Inschakelen Gezondheidscontroles", + "enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.", + "healthScheme": "Methode", + "healthSelectScheme": "Selecteer methode", + "healthCheckPath": "Pad", + "healthHostname": "IP / Hostnaam", + "healthPort": "Poort", + "healthCheckPathDescription": "Het pad om de gezondheid status te controleren.", + "healthyIntervalSeconds": "Gezonde Interval", + "unhealthyIntervalSeconds": "Ongezonde Interval", + "IntervalSeconds": "Gezonde Interval", "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "timeIsInSeconds": "Tijd is in seconden", + "retryAttempts": "Herhaal Pogingen", + "expectedResponseCodes": "Verwachte Reactiecodes", + "expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.", "customHeaders": "Aangepaste headers", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Kopregeleinde: Header-Naam: waarde", + "headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.", + "saveHealthCheck": "Opslaan Gezondheidscontrole", + "healthCheckSaved": "Gezondheidscontrole Opgeslagen", + "healthCheckSavedDescription": "Gezondheidscontrole configuratie succesvol opgeslagen", + "healthCheckError": "Gezondheidscontrole Fout", + "healthCheckErrorDescription": "Er is een fout opgetreden bij het opslaan van de configuratie van de gezondheidscontrole.", + "healthCheckPathRequired": "Gezondheidscontrole pad is vereist", + "healthCheckMethodRequired": "HTTP methode is vereist", + "healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn", + "healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn", + "healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn", "httpMethod": "HTTP-methode", "selectHttpMethod": "Selecteer HTTP-methode", "domainPickerSubdomainLabel": "Subdomein", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Voer een subdomein in om te zoeken en te selecteren uit beschikbare gratis domeinen.", "domainPickerFreeDomains": "Gratis Domeinen", "domainPickerSearchForAvailableDomains": "Zoek naar beschikbare domeinen", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Opmerking: Gratis aangeboden domeinen zijn momenteel niet beschikbaar voor zelf-gehoste instanties.", "resourceDomain": "Domein", "resourceEditDomain": "Domein bewerken", "siteName": "Site Naam", @@ -1543,72 +1543,72 @@ "autoLoginError": "Auto Login Fout", "autoLoginErrorNoRedirectUrl": "Geen redirect URL ontvangen van de identity provider.", "autoLoginErrorGeneratingUrl": "Genereren van authenticatie-URL mislukt.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodeManageRemoteExitNodes": "Beheer Zelf-Gehoste", + "remoteExitNodeDescription": "Beheer knooppunten om uw netwerkverbinding uit te breiden", "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", + "searchRemoteExitNodes": "Knooppunten zoeken...", + "remoteExitNodeAdd": "Voeg node toe", + "remoteExitNodeErrorDelete": "Fout bij verwijderen node", + "remoteExitNodeQuestionRemove": "Weet u zeker dat u het node {selectedNode} uit de organisatie wilt verwijderen?", + "remoteExitNodeMessageRemove": "Eenmaal verwijderd, zal het knooppunt niet langer toegankelijk zijn.", + "remoteExitNodeMessageConfirm": "Om te bevestigen, typ de naam van het knooppunt hieronder.", + "remoteExitNodeConfirmDelete": "Bevestig verwijderen node", + "remoteExitNodeDelete": "Knoop verwijderen", "sidebarRemoteExitNodes": "Nodes", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Maak node", + "description": "Maak een nieuwe node aan om uw netwerkverbinding uit te breiden", + "viewAllButton": "Alle nodes weergeven", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Creatie Strategie", + "description": "Kies dit om uw node handmatig te configureren of nieuwe referenties te genereren.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adopteer Node", + "description": "Kies dit als u al de referenties voor deze node heeft" }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Genereer Sleutels", + "description": "Kies dit als u nieuwe sleutels voor het knooppunt wilt genereren" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Adopteer Bestaande Node", + "description": "Voer de referenties in van het bestaande knooppunt dat u wilt adopteren", + "nodeIdLabel": "Knooppunt ID", + "nodeIdDescription": "De ID van het knooppunt dat u wilt adopteren", + "secretLabel": "Geheim", + "secretDescription": "De geheime sleutel van de bestaande node", + "submitButton": "Knooppunt adopteren" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Gegeneerde Inloggegevens", + "description": "Gebruik deze gegenereerde inloggegevens om uw node te configureren", + "nodeIdTitle": "Knooppunt ID", + "secretTitle": "Geheim", + "saveCredentialsTitle": "Voeg Inloggegevens toe aan Config", + "saveCredentialsDescription": "Voeg deze inloggegevens toe aan uw zelf-gehoste Pangolin-node configuratiebestand om de verbinding te voltooien.", + "submitButton": "Maak node" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "Node ID en Secret zijn verplicht bij het overnemen van een bestaand knooppunt" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Niet gelukt om standaarden te laden", + "defaultsNotLoaded": "Standaarden niet geladen", + "createFailed": "Fout bij het maken van node" }, "success": { - "created": "Node created successfully" + "created": "Node succesvol aangemaakt" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", + "remoteExitNodeSelection": "Knooppunt selectie", + "remoteExitNodeSelectionDescription": "Selecteer een node om het verkeer door te leiden voor deze lokale site", + "remoteExitNodeRequired": "Een node moet worden geselecteerd voor lokale sites", + "noRemoteExitNodesAvailable": "Geen knooppunten beschikbaar", + "noRemoteExitNodesAvailableDescription": "Er zijn geen knooppunten beschikbaar voor deze organisatie. Maak eerst een knooppunt aan om lokale sites te gebruiken.", "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "country": "Land", + "rulesMatchCountry": "Momenteel gebaseerd op bron IP", "managedSelfHosted": { "title": "Beheerde Self-Hosted", "description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internationaal Domein Gedetecteerd", "willbestoredas": "Zal worden opgeslagen als:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Bepaal hoe rollen worden toegewezen aan gebruikers wanneer ze inloggen wanneer Auto Provision is ingeschakeld.", + "selectRole": "Selecteer een rol", + "roleMappingExpression": "Expressie", + "selectRolePlaceholder": "Kies een rol", + "selectRoleDescription": "Selecteer een rol om toe te wijzen aan alle gebruikers van deze identiteitsprovider", + "roleMappingExpressionDescription": "Voer een JMESPath expressie in om rolinformatie van de ID-token te extraheren", + "idpTenantIdRequired": "Tenant ID is vereist", + "invalidValue": "Ongeldige waarde", + "idpTypeLabel": "Identiteit provider type", + "roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'", + "idpGoogleConfiguration": "Google Configuratie", + "idpGoogleConfigurationDescription": "Configureer uw Google OAuth2-referenties", + "idpGoogleClientIdDescription": "Uw Google OAuth2-client-ID", + "idpGoogleClientSecretDescription": "Uw Google OAuth2 Clientgeheim", + "idpAzureConfiguration": "Azure Entra ID configuratie", + "idpAzureConfigurationDescription": "Configureer uw Azure Entra ID OAuth2 referenties", + "idpTenantId": "Tenant-ID", + "idpTenantIdPlaceholder": "jouw-tenant-id", + "idpAzureTenantIdDescription": "Uw Azure tenant ID (gevonden in Azure Active Directory overzicht)", + "idpAzureClientIdDescription": "Uw Azure App registratie Client ID", + "idpAzureClientSecretDescription": "Uw Azure App registratie Client Secret", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google Configuratie", + "idpAzureConfigurationTitle": "Azure Entra ID configuratie", + "idpTenantIdLabel": "Tenant-ID", + "idpAzureClientIdDescription2": "Uw Azure App registratie Client ID", + "idpAzureClientSecretDescription2": "Uw Azure App registratie Client Secret", "idpGoogleDescription": "Google OAuth2/OIDC provider", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.", + "authPage": "Authenticatie pagina", + "authPageDescription": "De autorisatiepagina voor uw organisatie configureren", + "authPageDomain": "Authenticatie pagina domein", + "noDomainSet": "Geen domein ingesteld", + "changeDomain": "Domein wijzigen", + "selectDomain": "Domein selecteren", + "restartCertificate": "Certificaat opnieuw starten", + "editAuthPageDomain": "Authenticatiepagina domein bewerken", + "setAuthPageDomain": "Authenticatiepagina domein instellen", + "failedToFetchCertificate": "Certificaat ophalen mislukt", + "failedToRestartCertificate": "Kon certificaat niet opnieuw opstarten", + "addDomainToEnableCustomAuthPages": "Voeg een domein toe om aangepaste authenticatiepagina's voor uw organisatie in te schakelen", + "selectDomainForOrgAuthPage": "Selecteer een domein voor de authenticatiepagina van de organisatie", "domainPickerProvidedDomain": "Opgegeven domein", "domainPickerFreeProvidedDomain": "Gratis verstrekt domein", "domainPickerVerified": "Geverifieerd", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kon niet geldig worden gemaakt voor {domain}.", "domainPickerSubdomainSanitized": "Subdomein gesaniseerd", "domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Meld je aan bij je organisatie", + "orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan", + "orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.", + "orgAuthSignInWithPangolin": "Log in met Pangolin", + "subscriptionRequiredToUse": "Een abonnement is vereist om deze functie te gebruiken.", + "idpDisabled": "Identiteitsaanbieders zijn uitgeschakeld.", + "orgAuthPageDisabled": "Pagina voor organisatie-authenticatie is uitgeschakeld.", + "domainRestartedDescription": "Domeinverificatie met succes opnieuw gestart", "resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml", "emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", "twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Er is een fout opgetreden bij het bijwerken van de instellingen van de auth-pagina", + "authPageUpdated": "Auth-pagina succesvol bijgewerkt", + "healthCheckNotAvailable": "Lokaal", + "rewritePath": "Herschrijf Pad", + "rewritePathDescription": "Optioneel het pad herschrijven voordat je het naar het doel doorstuurt." } From 65bf055e0f55fed19ddffb0969a16ae4e8021834 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:01 -0700 Subject: [PATCH 149/322] New translations en-us.json (Polish) --- messages/pl-PL.json | 372 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 90d188b4..c3db35f5 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Łatwiejszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.", "siteWg": "Podstawowy WireGuard", "siteWgDescription": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana jest ręczna konfiguracja NAT.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana ręczna konfiguracja NAT. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH", "siteLocalDescription": "Tylko lokalne zasoby. Brak tunelu.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Tylko zasoby lokalne. Brak tunelowania. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH", "siteSeeAll": "Zobacz wszystkie witryny", "siteTunnelDescription": "Określ jak chcesz połączyć się ze swoją stroną", "siteNewtCredentials": "Aktualne dane logowania", @@ -159,7 +159,7 @@ "resourceHTTP": "Zasób HTTPS", "resourceHTTPDescription": "Proxy do Twojej aplikacji przez HTTPS, przy użyciu poddomeny lub domeny bazowej.", "resourceRaw": "Surowy zasób TCP/UDP", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Proxy do aplikacji przez TCP/UDP przy użyciu numeru portu.", "resourceCreate": "Utwórz zasób", "resourceCreateDescription": "Wykonaj poniższe kroki, aby utworzyć nowy zasób", "resourceSeeAll": "Zobacz wszystkie zasoby", @@ -168,9 +168,9 @@ "siteSelect": "Wybierz witrynę", "siteSearch": "Szukaj witryny", "siteNotFound": "Nie znaleziono witryny.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Wybierz kraj", + "searchCountries": "Szukaj krajów...", + "noCountryFound": "Nie znaleziono kraju.", "siteSelectionDescription": "Ta strona zapewni połączenie z celem.", "resourceType": "Typ zasobu", "resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu", @@ -1156,7 +1156,7 @@ "containerLabels": "Etykiety", "containerLabelsCount": "{count, plural, one {# etykieta} few {# etykiety} many {# etykiet} other {# etykiet}}", "containerLabelsTitle": "Etykiety kontenera", - "containerLabelEmpty": "", + "containerLabelEmpty": "", "containerPorts": "Porty", "containerPortsMore": "+{count} więcej", "containerActions": "Akcje", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdomena: {subdomain}", "domainPickerNamespace": "Przestrzeń nazw: {namespace}", "domainPickerShowMore": "Pokaż więcej", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Wybierz region", + "regionSelectorInfo": "Wybór regionu pomaga nam zapewnić lepszą wydajność dla Twojej lokalizacji. Nie musisz być w tym samym regionie co Twój serwer.", + "regionSelectorPlaceholder": "Wybierz region", + "regionSelectorComingSoon": "Wkrótce dostępne", + "billingLoadingSubscription": "Ładowanie subskrypcji...", + "billingFreeTier": "Darmowy pakiet", + "billingWarningOverLimit": "Ostrzeżenie: Przekroczyłeś jeden lub więcej limitów użytkowania. Twoje witryny nie połączą się, dopóki nie zmienisz subskrypcji lub nie dostosujesz użytkowania.", + "billingUsageLimitsOverview": "Przegląd Limitów Użytkowania", + "billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@fossorial.io.", + "billingDataUsage": "Użycie danych", + "billingOnlineTime": "Czas Online Strony", + "billingUsers": "Aktywni użytkownicy", + "billingDomains": "Aktywne domeny", + "billingRemoteExitNodes": "Aktywne samodzielnie-hostowane węzły", + "billingNoLimitConfigured": "Nie skonfigurowano limitu", + "billingEstimatedPeriod": "Szacowany Okres Rozliczeniowy", + "billingIncludedUsage": "Zawarte użycie", + "billingIncludedUsageDescription": "Użycie zawarte w obecnym planie subskrypcji", + "billingFreeTierIncludedUsage": "Limity użycia dla darmowego pakietu", + "billingIncluded": "zawarte", + "billingEstimatedTotal": "Szacowana Całkowita:", + "billingNotes": "Notatki", + "billingEstimateNote": "To jest szacunkowe, oparte na Twoim obecnym użyciu.", + "billingActualChargesMayVary": "Rzeczywiste opłaty mogą się różnić.", + "billingBilledAtEnd": "Zostaniesz obciążony na koniec okresu rozliczeniowego.", + "billingModifySubscription": "Modyfikuj Subskrypcję", + "billingStartSubscription": "Rozpocznij Subskrypcję", + "billingRecurringCharge": "Opłata Cyklowa", + "billingManageSubscriptionSettings": "Zarządzaj ustawieniami i preferencjami subskrypcji", + "billingNoActiveSubscription": "Nie masz aktywnej subskrypcji. Rozpocznij subskrypcję, aby zwiększyć limity użytkowania.", + "billingFailedToLoadSubscription": "Nie udało się załadować subskrypcji", + "billingFailedToLoadUsage": "Nie udało się załadować użycia", + "billingFailedToGetCheckoutUrl": "Nie udało się uzyskać adresu URL zakupu", + "billingPleaseTryAgainLater": "Spróbuj ponownie później.", + "billingCheckoutError": "Błąd przy kasie", + "billingFailedToGetPortalUrl": "Nie udało się uzyskać adresu URL portalu", + "billingPortalError": "Błąd Portalu", + "billingDataUsageInfo": "Jesteś obciążony za wszystkie dane przesyłane przez bezpieczne tunele, gdy jesteś podłączony do chmury. Obejmuje to zarówno ruch przychodzący, jak i wychodzący we wszystkich Twoich witrynach. Gdy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie ograniczysz użycia. Dane nie będą naliczane przy użyciu węzłów.", + "billingOnlineTimeInfo": "Opłata zależy od tego, jak długo twoje strony pozostają połączone z chmurą. Na przykład 44,640 minut oznacza jedną stronę działającą 24/7 przez cały miesiąc. Kiedy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie zmniejsz jego wykorzystania. Czas nie będzie naliczany przy użyciu węzłów.", + "billingUsersInfo": "Jesteś obciążany za każdego użytkownika w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont użytkowników w twojej organizacji.", + "billingDomainInfo": "Jesteś obciążany za każdą domenę w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont domen w twojej organizacji.", + "billingRemoteExitNodesInfo": "Jesteś obciążany za każdy zarządzany węzeł w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych zarządzanych węzłów w twojej organizacji.", "domainNotFound": "Nie znaleziono domeny", "domainNotFoundDescription": "Zasób jest wyłączony, ponieważ domena nie istnieje już w naszym systemie. Proszę ustawić nową domenę dla tego zasobu.", "failed": "Niepowodzenie", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Zmiany DNS mogą zająć trochę czasu na rozpropagowanie się w Internecie. Może to potrwać od kilku minut do 48 godzin, w zależności od dostawcy DNS i ustawień TTL.", "resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP", "resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Kalkulator Cen", "signUpTerms": { "IAgreeToThe": "Zgadzam się z", "termsOfService": "warunkami usługi", @@ -1412,41 +1412,41 @@ "addNewTarget": "Dodaj nowy cel", "targetsList": "Lista celów", "targetErrorDuplicateTargetFound": "Znaleziono duplikat celu", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", + "healthCheckHealthy": "Zdrowy", + "healthCheckUnhealthy": "Niezdrowy", + "healthCheckUnknown": "Nieznany", + "healthCheck": "Kontrola Zdrowia", + "configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia", + "configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}", + "enableHealthChecks": "Włącz Kontrole Zdrowia", + "enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.", + "healthScheme": "Metoda", + "healthSelectScheme": "Wybierz metodę", + "healthCheckPath": "Ścieżka", + "healthHostname": "IP / Nazwa hosta", "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckPathDescription": "Ścieżka do sprawdzania stanu zdrowia.", + "healthyIntervalSeconds": "Interwał Zdrowy", + "unhealthyIntervalSeconds": "Interwał Niezdrowy", + "IntervalSeconds": "Interwał Zdrowy", + "timeoutSeconds": "Limit Czasu", + "timeIsInSeconds": "Czas w sekundach", + "retryAttempts": "Próby Ponowienia", + "expectedResponseCodes": "Oczekiwane Kody Odpowiedzi", + "expectedResponseCodesDescription": "Kod statusu HTTP, który wskazuje zdrowy status. Jeśli pozostanie pusty, uznaje się 200-300 za zdrowy.", "customHeaders": "Niestandardowe nagłówki", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Nagłówki oddzielone: Nazwa nagłówka: wartość", + "headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.", + "saveHealthCheck": "Zapisz Kontrolę Zdrowia", + "healthCheckSaved": "Kontrola Zdrowia Zapisana", + "healthCheckSavedDescription": "Konfiguracja kontroli zdrowia została zapisana pomyślnie", + "healthCheckError": "Błąd Kontroli Zdrowia", + "healthCheckErrorDescription": "Wystąpił błąd podczas zapisywania konfiguracji kontroli zdrowia", + "healthCheckPathRequired": "Ścieżka kontroli zdrowia jest wymagana", + "healthCheckMethodRequired": "Metoda HTTP jest wymagana", + "healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund", + "healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę", + "healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1", "httpMethod": "Metoda HTTP", "selectHttpMethod": "Wybierz metodę HTTP", "domainPickerSubdomainLabel": "Poddomena", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Wprowadź poddomenę, aby wyszukać i wybrać z dostępnych darmowych domen.", "domainPickerFreeDomains": "Darmowe domeny", "domainPickerSearchForAvailableDomains": "Szukaj dostępnych domen", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Uwaga: Darmowe domeny nie są obecnie dostępne dla instancji samodzielnie-hostowanych.", "resourceDomain": "Domena", "resourceEditDomain": "Edytuj domenę", "siteName": "Nazwa strony", @@ -1543,72 +1543,72 @@ "autoLoginError": "Błąd automatycznego logowania", "autoLoginErrorNoRedirectUrl": "Nie otrzymano URL przekierowania od dostawcy tożsamości.", "autoLoginErrorGeneratingUrl": "Nie udało się wygenerować URL uwierzytelniania.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Zarządzaj Samodzielnie-Hostingowane", + "remoteExitNodeDescription": "Zarządzaj węzłami w celu rozszerzenia połączenia z siecią", + "remoteExitNodes": "Węzły", + "searchRemoteExitNodes": "Szukaj węzłów...", + "remoteExitNodeAdd": "Dodaj węzeł", + "remoteExitNodeErrorDelete": "Błąd podczas usuwania węzła", + "remoteExitNodeQuestionRemove": "Czy na pewno chcesz usunąć węzeł {selectedNode} z organizacji?", + "remoteExitNodeMessageRemove": "Po usunięciu, węzeł nie będzie już dostępny.", + "remoteExitNodeMessageConfirm": "Aby potwierdzić, wpisz nazwę węzła poniżej.", + "remoteExitNodeConfirmDelete": "Potwierdź usunięcie węzła", + "remoteExitNodeDelete": "Usuń węzeł", + "sidebarRemoteExitNodes": "Węzły", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Utwórz węzeł", + "description": "Utwórz nowy węzeł, aby rozszerzyć połączenie z siecią", + "viewAllButton": "Zobacz wszystkie węzły", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Strategia Tworzenia", + "description": "Wybierz to, aby ręcznie skonfigurować węzeł lub wygenerować nowe poświadczenia.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Zaadoptuj Węzeł", + "description": "Wybierz to, jeśli masz już dane logowania dla węzła." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Generuj Klucze", + "description": "Wybierz to, jeśli chcesz wygenerować nowe klucze dla węzła" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Zaadoptuj Istniejący Węzeł", + "description": "Wprowadź dane logowania istniejącego węzła, który chcesz przyjąć", + "nodeIdLabel": "ID węzła", + "nodeIdDescription": "ID istniejącego węzła, który chcesz przyjąć", + "secretLabel": "Sekret", + "secretDescription": "Sekretny klucz istniejącego węzła", + "submitButton": "Przyjmij węzeł" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Wygenerowane Poświadczenia", + "description": "Użyj tych danych logowania, aby skonfigurować węzeł", + "nodeIdTitle": "ID węzła", + "secretTitle": "Sekret", + "saveCredentialsTitle": "Dodaj Poświadczenia do Konfiguracji", + "saveCredentialsDescription": "Dodaj te poświadczenia do pliku konfiguracyjnego swojego samodzielnie-hostowanego węzła Pangolin, aby zakończyć połączenie.", + "submitButton": "Utwórz węzeł" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "Identyfikator węzła i sekret są wymagane podczas przyjmowania istniejącego węzła" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Nie udało się załadować domyślnych ustawień", + "defaultsNotLoaded": "Domyślne ustawienia nie zostały załadowane", + "createFailed": "Nie udało się utworzyć węzła" }, "success": { - "created": "Node created successfully" + "created": "Węzeł utworzony pomyślnie" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Wybór węzła", + "remoteExitNodeSelectionDescription": "Wybierz węzeł do przekierowania ruchu dla tej lokalnej witryny", + "remoteExitNodeRequired": "Węzeł musi być wybrany dla lokalnych witryn", + "noRemoteExitNodesAvailable": "Brak dostępnych węzłów", + "noRemoteExitNodesAvailableDescription": "Węzły nie są dostępne dla tej organizacji. Utwórz węzeł, aby używać lokalnych witryn.", + "exitNode": "Węzeł Wyjściowy", + "country": "Kraj", + "rulesMatchCountry": "Obecnie bazuje na adresie IP źródła", "managedSelfHosted": { "title": "Zarządzane Samodzielnie-Hostingowane", "description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Wykryto międzynarodową domenę", "willbestoredas": "Będą przechowywane jako:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Określ jak role są przypisywane do użytkowników podczas logowania się, gdy automatyczne świadczenie jest włączone.", + "selectRole": "Wybierz rolę", + "roleMappingExpression": "Wyrażenie", + "selectRolePlaceholder": "Wybierz rolę", + "selectRoleDescription": "Wybierz rolę do przypisania wszystkim użytkownikom od tego dostawcy tożsamości", + "roleMappingExpressionDescription": "Wprowadź wyrażenie JMESŚcieżki, aby wyodrębnić informacje o roli z tokenu ID", + "idpTenantIdRequired": "ID lokatora jest wymagane", + "invalidValue": "Nieprawidłowa wartość", + "idpTypeLabel": "Typ dostawcy tożsamości", + "roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'", + "idpGoogleConfiguration": "Konfiguracja Google", + "idpGoogleConfigurationDescription": "Skonfiguruj swoje poświadczenia Google OAuth2", + "idpGoogleClientIdDescription": "Twój identyfikator klienta Google OAuth2", + "idpGoogleClientSecretDescription": "Twój klucz klienta Google OAuth2", + "idpAzureConfiguration": "Konfiguracja Azure Entra ID", + "idpAzureConfigurationDescription": "Skonfiguruj swoje dane logowania OAuth2 Azure Entra", + "idpTenantId": "ID Najemcy", + "idpTenantIdPlaceholder": "twoj-lokator", + "idpAzureTenantIdDescription": "Twój identyfikator dzierżawcy Azure (znaleziony w Podglądzie Azure Active Directory", + "idpAzureClientIdDescription": "Twój identyfikator klienta rejestracji aplikacji Azure", + "idpAzureClientSecretDescription": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Konfiguracja Google", + "idpAzureConfigurationTitle": "Konfiguracja Azure Entra ID", + "idpTenantIdLabel": "ID Najemcy", + "idpAzureClientIdDescription2": "Twój identyfikator klienta rejestracji aplikacji Azure", + "idpAzureClientSecretDescription2": "Klucz tajny Twojego klienta rejestracji aplikacji Azure", "idpGoogleDescription": "Dostawca Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Podsieć", + "subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.", + "authPage": "Strona uwierzytelniania", + "authPageDescription": "Skonfiguruj stronę uwierzytelniania dla swojej organizacji", + "authPageDomain": "Domena strony uwierzytelniania", + "noDomainSet": "Nie ustawiono domeny", + "changeDomain": "Zmień domenę", + "selectDomain": "Wybierz domenę", + "restartCertificate": "Uruchom ponownie certyfikat", + "editAuthPageDomain": "Edytuj domenę strony uwierzytelniania", + "setAuthPageDomain": "Ustaw domenę strony uwierzytelniania", + "failedToFetchCertificate": "Nie udało się pobrać certyfikatu", + "failedToRestartCertificate": "Nie udało się ponownie uruchomić certyfikatu", + "addDomainToEnableCustomAuthPages": "Dodaj domenę, aby włączyć niestandardowe strony uwierzytelniania dla Twojej organizacji", + "selectDomainForOrgAuthPage": "Wybierz domenę dla strony uwierzytelniania organizacji", "domainPickerProvidedDomain": "Dostarczona domena", "domainPickerFreeProvidedDomain": "Darmowa oferowana domena", "domainPickerVerified": "Zweryfikowano", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nie może być poprawne dla {domain}.", "domainPickerSubdomainSanitized": "Poddomena oczyszczona", "domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Zaloguj się do swojej organizacji", + "orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować", + "orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.", + "orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin", + "subscriptionRequiredToUse": "Do korzystania z tej funkcji wymagana jest subskrypcja.", + "idpDisabled": "Dostawcy tożsamości są wyłączeni", + "orgAuthPageDisabled": "Strona autoryzacji organizacji jest wyłączona.", + "domainRestartedDescription": "Weryfikacja domeny zrestartowana pomyślnie", "resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml", "emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.", "twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Wystąpił błąd podczas aktualizacji ustawień strony uwierzytelniania", + "authPageUpdated": "Strona uwierzytelniania została pomyślnie zaktualizowana", + "healthCheckNotAvailable": "Lokalny", + "rewritePath": "Przepis Ścieżki", + "rewritePathDescription": "Opcjonalnie przepisz ścieżkę przed przesłaniem do celu." } From 0f466515003ab319540beb9d57f7b2f46fd873b6 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:03 -0700 Subject: [PATCH 150/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 644 ++++++++++++++++++++++---------------------- 1 file changed, 322 insertions(+), 322 deletions(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 3c5ae253..ff0c093d 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -8,25 +8,25 @@ "orgId": "ID da organização", "setupIdentifierMessage": "Este é o identificador exclusivo para sua organização. Isso é separado do nome de exibição.", "setupErrorIdentifier": "O ID da organização já existe. Por favor, escolha um diferente.", - "componentsErrorNoMemberCreate": "Você não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", - "componentsErrorNoMember": "Você não é atualmente um membro de nenhuma organização.", + "componentsErrorNoMemberCreate": "Não é atualmente um membro de nenhuma organização. Crie uma organização para começar.", + "componentsErrorNoMember": "Não é atualmente um membro de nenhuma organização.", "welcome": "Bem-vindo ao Pangolin", "welcomeTo": "Bem-vindo ao", "componentsCreateOrg": "Criar uma organização", - "componentsMember": "Você é membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", + "componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", - "dismiss": "Descartar", + "dismiss": "Rejeitar", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", - "inviteErrorNotValid": "Desculpe, mas parece que o convite que você está tentando acessar não foi aceito ou não é mais válido.", - "inviteErrorUser": "Lamentamos, mas parece que o convite que você está tentando acessar não é para este usuário.", - "inviteLoginUser": "Verifique se você está logado como o usuário correto.", - "inviteErrorNoUser": "Desculpe, mas parece que o convite que você está tentando acessar não é para um usuário que existe.", + "inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.", + "inviteErrorUser": "Lamentamos, mas parece que o convite que está a tentar aceder não é para este utilizador.", + "inviteLoginUser": "Verifique se você está logado como o utilizador correto.", + "inviteErrorNoUser": "Desculpe, mas parece que o convite que está a tentar aceder não é para um utilizador que existe.", "inviteCreateUser": "Por favor, crie uma conta primeiro.", - "goHome": "Ir para casa", - "inviteLogInOtherUser": "Fazer login como um usuário diferente", + "goHome": "Voltar ao inicio", + "inviteLogInOtherUser": "Fazer login como um utilizador diferente", "createAnAccount": "Crie uma conta", - "inviteNotAccepted": "Convite não aceito", + "inviteNotAccepted": "Convite não aceite", "authCreateAccount": "Crie uma conta para começar", "authNoAccount": "Não possui uma conta?", "email": "e-mail", @@ -34,23 +34,23 @@ "confirmPassword": "Confirmar senha", "createAccount": "Criar conta", "viewSettings": "Visualizar configurações", - "delete": "excluir", + "delete": "apagar", "name": "Nome:", "online": "Disponível", "offline": "Desconectado", "site": "site", - "dataIn": "Dados em", + "dataIn": "Dados de entrada", "dataOut": "Dados de saída", "connectionType": "Tipo de conexão", "tunnelType": "Tipo de túnel", "local": "Localização", "edit": "Alterar", - "siteConfirmDelete": "Confirmar exclusão do site", + "siteConfirmDelete": "Confirmar que pretende apagar o site", "siteDelete": "Excluir site", "siteMessageRemove": "Uma vez removido, o site não estará mais acessível. Todos os recursos e alvos associados ao site também serão removidos.", "siteMessageConfirm": "Para confirmar, por favor, digite o nome do site abaixo.", "siteQuestionRemove": "Você tem certeza que deseja remover o site {selectedSite} da organização?", - "siteManageSites": "Gerenciar sites", + "siteManageSites": "Gerir sites", "siteDescription": "Permitir conectividade à sua rede através de túneis seguros", "siteCreate": "Criar site", "siteCreateDescription2": "Siga os passos abaixo para criar e conectar um novo site", @@ -79,10 +79,10 @@ "operatingSystem": "Sistema operacional", "commands": "Comandos", "recommended": "Recomendados", - "siteNewtDescription": "Para a melhor experiência do usuário, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", + "siteNewtDescription": "Para a melhor experiência do utilizador, utilize Novo. Ele usa o WireGuard sob o capuz e permite que você aborde seus recursos privados através dos endereços LAN em sua rede privada do painel do Pangolin.", "siteRunsInDocker": "Executa no Docker", "siteRunsInShell": "Executa na shell no macOS, Linux e Windows", - "siteErrorDelete": "Erro ao excluir site", + "siteErrorDelete": "Erro ao apagar site", "siteErrorUpdate": "Falha ao atualizar site", "siteErrorUpdateDescription": "Ocorreu um erro ao atualizar o site.", "siteUpdated": "Site atualizado", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "A maneira mais fácil de criar um ponto de entrada na sua rede. Nenhuma configuração extra.", "siteWg": "WireGuard Básico", "siteWgDescription": "Use qualquer cliente do WireGuard para estabelecer um túnel. Configuração manual NAT é necessária.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Use qualquer cliente WireGuard para estabelecer um túnel. Configuração manual NAT necessária. SOMENTE FUNCIONA EM NODES AUTO-HOSPEDADOS", "siteLocalDescription": "Recursos locais apenas. Sem túneis.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Apenas recursos locais. Sem tunelamento. SOMENTE FUNCIONA EM NODES AUTO-HOSPEDADOS", "siteSeeAll": "Ver todos os sites", "siteTunnelDescription": "Determine como você deseja se conectar ao seu site", "siteNewtCredentials": "Credenciais Novas", @@ -105,12 +105,12 @@ "siteCredentialsSaveDescription": "Você só será capaz de ver esta vez. Certifique-se de copiá-lo para um lugar seguro.", "siteInfo": "Informações do Site", "status": "SItuação", - "shareTitle": "Gerenciar links de compartilhamento", + "shareTitle": "Gerir links partilhados", "shareDescription": "Criar links compartilháveis para conceder acesso temporário ou permanente aos seus recursos", "shareSearch": "Pesquisar links de compartilhamento...", "shareCreate": "Criar Link de Compartilhamento", - "shareErrorDelete": "Falha ao excluir o link", - "shareErrorDeleteMessage": "Ocorreu um erro ao excluir o link", + "shareErrorDelete": "Falha ao apagar o link", + "shareErrorDeleteMessage": "Ocorreu um erro ao apagar o link", "shareDeleted": "Link excluído", "shareDeletedDescription": "O link foi eliminado", "shareTokenDescription": "Seu token de acesso pode ser passado de duas maneiras: como um parâmetro de consulta ou nos cabeçalhos da solicitação. Estes devem ser passados do cliente em todas as solicitações para acesso autenticado.", @@ -127,13 +127,13 @@ "shareErrorFetchResourceDescription": "Ocorreu um erro ao obter os recursos", "shareErrorCreate": "Falha ao criar link de compartilhamento", "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", - "shareCreateDescription": "Qualquer um com este link pode acessar o recurso", + "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", "shareTitleOptional": "Título (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", - "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os usuários que usaram este link perderão acesso ao recurso.", + "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", "shareSeeOnce": "Você só poderá ver este link uma vez. Certifique-se de copiá-lo.", - "shareAccessHint": "Qualquer um com este link pode acessar o recurso. Compartilhe com cuidado.", + "shareAccessHint": "Qualquer um com este link pode aceder o recurso. Compartilhe com cuidado.", "shareTokenUsage": "Ver Uso do Token de Acesso", "createLink": "Criar Link", "resourcesNotFound": "Nenhum recurso encontrado", @@ -145,11 +145,11 @@ "expires": "Expira", "never": "nunca", "shareErrorSelectResource": "Por favor, selecione um recurso", - "resourceTitle": "Gerenciar Recursos", + "resourceTitle": "Gerir Recursos", "resourceDescription": "Crie proxies seguros para seus aplicativos privados", "resourcesSearch": "Procurar recursos...", "resourceAdd": "Adicionar Recurso", - "resourceErrorDelte": "Erro ao excluir recurso", + "resourceErrorDelte": "Erro ao apagar recurso", "authentication": "Autenticação", "protected": "Protegido", "notProtected": "Não Protegido", @@ -159,7 +159,7 @@ "resourceHTTP": "Recurso HTTPS", "resourceHTTPDescription": "O proxy solicita ao seu aplicativo via HTTPS usando um subdomínio ou domínio base.", "resourceRaw": "Recurso TCP/UDP bruto", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "O proxy solicita ao seu aplicativo sobre TCP/UDP usando um número de porta.", "resourceCreate": "Criar Recurso", "resourceCreateDescription": "Siga os passos abaixo para criar um novo recurso", "resourceSeeAll": "Ver todos os recursos", @@ -168,12 +168,12 @@ "siteSelect": "Selecionar site", "siteSearch": "Procurar no site", "siteNotFound": "Nenhum site encontrado.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Selecionar país", + "searchCountries": "Buscar países...", + "noCountryFound": "Nenhum país encontrado.", "siteSelectionDescription": "Este site fornecerá conectividade ao destino.", "resourceType": "Tipo de Recurso", - "resourceTypeDescription": "Determine como você deseja acessar seu recurso", + "resourceTypeDescription": "Determine como você deseja aceder seu recurso", "resourceHTTPSSettings": "Configurações de HTTPS", "resourceHTTPSSettingsDescription": "Configure como seu recurso será acessado por HTTPS", "domainType": "Tipo de domínio", @@ -195,7 +195,7 @@ "resourceBack": "Voltar aos recursos", "resourceGoTo": "Ir para o Recurso", "resourceDelete": "Excluir Recurso", - "resourceDeleteConfirm": "Confirmar exclusão de recurso", + "resourceDeleteConfirm": "Confirmar que pretende apagar o recurso", "visibility": "Visibilidade", "enabled": "Ativado", "disabled": "Desabilitado", @@ -211,14 +211,14 @@ "passToAuth": "Passar para Autenticação", "orgSettingsDescription": "Configurar as configurações gerais da sua organização", "orgGeneralSettings": "Configurações da organização", - "orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização", - "saveGeneralSettings": "Salvar configurações gerais", - "saveSettings": "Salvar Configurações", + "orgGeneralSettingsDescription": "Gerir os detalhes e a configuração da sua organização", + "saveGeneralSettings": "Guardar configurações gerais", + "saveSettings": "Guardar Configurações", "orgDangerZone": "Zona de Perigo", "orgDangerZoneDescription": "Uma vez que você exclui esta organização, não há volta. Por favor, tenha certeza.", "orgDelete": "Excluir Organização", - "orgDeleteConfirm": "Confirmar exclusão da organização", - "orgMessageRemove": "Esta ação é irreversível e excluirá todos os dados associados.", + "orgDeleteConfirm": "Confirmar que pretende apagar a organização", + "orgMessageRemove": "Esta ação é irreversível e apagará todos os dados associados.", "orgMessageConfirm": "Para confirmar, digite o nome da organização abaixo.", "orgQuestionRemove": "Tem certeza que deseja remover a organização {selectedOrg}?", "orgUpdated": "Organização atualizada", @@ -227,29 +227,29 @@ "orgErrorUpdateMessage": "Ocorreu um erro ao atualizar a organização.", "orgErrorFetch": "Falha ao buscar organizações", "orgErrorFetchMessage": "Ocorreu um erro ao listar suas organizações", - "orgErrorDelete": "Falha ao excluir organização", - "orgErrorDeleteMessage": "Ocorreu um erro ao excluir a organização.", + "orgErrorDelete": "Falha ao apagar organização", + "orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.", "orgDeleted": "Organização excluída", "orgDeletedMessage": "A organização e seus dados foram excluídos.", "orgMissing": "ID da Organização Ausente", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", - "accessUsersManage": "Gerenciar Usuários", - "accessUsersDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à sua organização", - "accessUsersSearch": "Procurar usuários...", + "accessUsersManage": "Gerir Utilizadores", + "accessUsersDescription": "Convidar utilizadores e adicioná-los a funções para gerir o acesso à sua organização", + "accessUsersSearch": "Procurar utilizadores...", "accessUserCreate": "Criar Usuário", - "accessUserRemove": "Remover usuário", + "accessUserRemove": "Remover utilizador", "username": "Usuário:", "identityProvider": "Provedor de Identidade", "role": "Funções", "nameRequired": "O nome é obrigatório", - "accessRolesManage": "Gerenciar Funções", - "accessRolesDescription": "Configurar funções para gerenciar o acesso à sua organização", + "accessRolesManage": "Gerir Funções", + "accessRolesDescription": "Configurar funções para gerir o acesso à sua organização", "accessRolesSearch": "Pesquisar funções...", "accessRolesAdd": "Adicionar função", "accessRoleDelete": "Excluir Papel", "description": "Descrição:", "inviteTitle": "Convites Abertos", - "inviteDescription": "Gerencie seus convites para outros usuários", + "inviteDescription": "Gerir seus convites para outros utilizadores", "inviteSearch": "Procurar convites...", "minutes": "minutos", "hours": "horas", @@ -267,7 +267,7 @@ "apiKeysGeneralSettings": "Permissões", "apiKeysGeneralSettingsDescription": "Determine o que esta chave API pode fazer", "apiKeysList": "Sua Chave API", - "apiKeysSave": "Salvar Sua Chave API", + "apiKeysSave": "Guardar Sua Chave API", "apiKeysSaveDescription": "Você só poderá ver isto uma vez. Certifique-se de copiá-la para um local seguro.", "apiKeysInfo": "Sua chave API é:", "apiKeysConfirmCopy": "Eu copiei a chave API", @@ -280,33 +280,33 @@ "apiKeysPermissionsUpdatedDescription": "As permissões foram atualizadas.", "apiKeysPermissionsGeneralSettings": "Permissões", "apiKeysPermissionsGeneralSettingsDescription": "Determine o que esta chave API pode fazer", - "apiKeysPermissionsSave": "Salvar Permissões", + "apiKeysPermissionsSave": "Guardar Permissões", "apiKeysPermissionsTitle": "Permissões", "apiKeys": "Chaves API", "searchApiKeys": "Pesquisar chaves API...", "apiKeysAdd": "Gerar Chave API", - "apiKeysErrorDelete": "Erro ao excluir chave API", - "apiKeysErrorDeleteMessage": "Erro ao excluir chave API", + "apiKeysErrorDelete": "Erro ao apagar chave API", + "apiKeysErrorDeleteMessage": "Erro ao apagar chave API", "apiKeysQuestionRemove": "Tem certeza que deseja remover a chave API {selectedApiKey} da organização?", "apiKeysMessageRemove": "Uma vez removida, a chave API não poderá mais ser utilizada.", "apiKeysMessageConfirm": "Para confirmar, por favor digite o nome da chave API abaixo.", "apiKeysDeleteConfirm": "Confirmar Exclusão da Chave API", "apiKeysDelete": "Excluir Chave API", - "apiKeysManage": "Gerenciar Chaves API", + "apiKeysManage": "Gerir Chaves API", "apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração", "apiKeysSettings": "Configurações de {apiKeyName}", - "userTitle": "Gerenciar Todos os Usuários", - "userDescription": "Visualizar e gerenciar todos os usuários no sistema", + "userTitle": "Gerir Todos os Utilizadores", + "userDescription": "Visualizar e gerir todos os utilizadores no sistema", "userAbount": "Sobre a Gestão de Usuário", - "userAbountDescription": "Esta tabela exibe todos os objetos root do usuário. Cada usuário pode pertencer a várias organizações. Remover um usuário de uma organização não exclui seu objeto de usuário raiz - ele permanecerá no sistema. Para remover completamente um usuário do sistema, você deve excluir seu objeto raiz usando a ação de excluir nesta tabela.", - "userServer": "Usuários do Servidor", - "userSearch": "Pesquisar usuários do servidor...", - "userErrorDelete": "Erro ao excluir usuário", + "userAbountDescription": "Esta tabela exibe todos os objetos root do utilizador. Cada utilizador pode pertencer a várias organizações. Remover um utilizador de uma organização não exclui seu objeto de utilizador raiz - ele permanecerá no sistema. Para remover completamente um utilizador do sistema, você deve apagar seu objeto raiz usando a ação de apagar nesta tabela.", + "userServer": "Utilizadores do Servidor", + "userSearch": "Pesquisar utilizadores do servidor...", + "userErrorDelete": "Erro ao apagar utilizador", "userDeleteConfirm": "Confirmar Exclusão do Usuário", - "userDeleteServer": "Excluir usuário do servidor", - "userMessageRemove": "O usuário será removido de todas as organizações e será completamente removido do servidor.", - "userMessageConfirm": "Para confirmar, por favor digite o nome do usuário abaixo.", - "userQuestionRemove": "Tem certeza que deseja excluir o {selectedUser} permanentemente do servidor?", + "userDeleteServer": "Excluir utilizador do servidor", + "userMessageRemove": "O utilizador será removido de todas as organizações e será completamente removido do servidor.", + "userMessageConfirm": "Para confirmar, por favor digite o nome do utilizador abaixo.", + "userQuestionRemove": "Tem certeza que deseja apagar o {selectedUser} permanentemente do servidor?", "licenseKey": "Chave de Licença", "valid": "Válido", "numberOfSites": "Número de sites", @@ -317,8 +317,8 @@ "licenseTermsAgree": "Você deve concordar com os termos da licença", "licenseErrorKeyLoad": "Falha ao carregar chaves de licença", "licenseErrorKeyLoadDescription": "Ocorreu um erro ao carregar a chave da licença.", - "licenseErrorKeyDelete": "Falha ao excluir chave de licença", - "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao excluir a chave de licença.", + "licenseErrorKeyDelete": "Falha ao apagar chave de licença", + "licenseErrorKeyDeleteDescription": "Ocorreu um erro ao apagar a chave de licença.", "licenseKeyDeleted": "Chave da licença excluída", "licenseKeyDeletedDescription": "A chave da licença foi excluída.", "licenseErrorKeyActivate": "Falha ao ativar a chave de licença", @@ -339,13 +339,13 @@ "fossorialLicense": "Ver Termos e Condições de Assinatura e Licença Fossorial", "licenseMessageRemove": "Isto irá remover a chave da licença e todas as permissões associadas concedidas por ela.", "licenseMessageConfirm": "Para confirmar, por favor, digite a chave de licença abaixo.", - "licenseQuestionRemove": "Tem certeza que deseja excluir a chave de licença {selectedKey}?", + "licenseQuestionRemove": "Tem certeza que deseja apagar a chave de licença {selectedKey}?", "licenseKeyDelete": "Excluir Chave de Licença", - "licenseKeyDeleteConfirm": "Confirmar exclusão da chave de licença", - "licenseTitle": "Gerenciar Status da Licença", - "licenseTitleDescription": "Visualizar e gerenciar chaves de licença no sistema", + "licenseKeyDeleteConfirm": "Confirmar que pretende apagar a chave de licença", + "licenseTitle": "Gerir Status da Licença", + "licenseTitleDescription": "Visualizar e gerir chaves de licença no sistema", "licenseHost": "Licença do host", - "licenseHostDescription": "Gerenciar a chave de licença principal do host.", + "licenseHostDescription": "Gerir a chave de licença principal do host.", "licensedNot": "Não Licenciado", "hostId": "ID do host", "licenseReckeckAll": "Verifique novamente todas as chaves", @@ -373,37 +373,37 @@ "inviteRemoved": "Convite removido", "inviteRemovedDescription": "O convite para {email} foi removido.", "inviteQuestionRemove": "Tem certeza de que deseja remover o convite {email}?", - "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o usuário novamente mais tarde.", + "inviteMessageRemove": "Uma vez removido, este convite não será mais válido. Você sempre pode convidar o utilizador novamente mais tarde.", "inviteMessageConfirm": "Para confirmar, digite o endereço de e-mail do convite abaixo.", "inviteQuestionRegenerate": "Tem certeza que deseja regenerar o convite{email, plural, ='' {}, other { para #}}? Isso irá revogar o convite anterior.", "inviteRemoveConfirm": "Confirmar Remoção do Convite", "inviteRegenerated": "Convite Regenerado", "inviteSent": "Um novo convite foi enviado para {email}.", - "inviteSentEmail": "Enviar notificação por e-mail ao usuário", + "inviteSentEmail": "Enviar notificação por e-mail ao utilizador", "inviteGenerate": "Um novo convite foi gerado para {email}.", "inviteDuplicateError": "Convite Duplicado", - "inviteDuplicateErrorDescription": "Já existe um convite para este usuário.", + "inviteDuplicateErrorDescription": "Já existe um convite para este utilizador.", "inviteRateLimitError": "Limite de Taxa Excedido", - "inviteRateLimitErrorDescription": "Você excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", + "inviteRateLimitErrorDescription": "Excedeu o limite de 3 regenerações por hora. Por favor, tente novamente mais tarde.", "inviteRegenerateError": "Falha ao Regenerar Convite", "inviteRegenerateErrorDescription": "Ocorreu um erro ao regenerar o convite.", "inviteValidityPeriod": "Período de Validade", "inviteValidityPeriodSelect": "Selecione o período de validade", - "inviteRegenerateMessage": "O convite foi regenerado. O usuário deve acessar o link abaixo para aceitar o convite.", + "inviteRegenerateMessage": "O convite foi regenerado. O utilizador deve aceder o link abaixo para aceitar o convite.", "inviteRegenerateButton": "Regenerar", "expiresAt": "Expira em", "accessRoleUnknown": "Função Desconhecida", "placeholder": "Espaço reservado", - "userErrorOrgRemove": "Falha ao remover usuário", - "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o usuário.", + "userErrorOrgRemove": "Falha ao remover utilizador", + "userErrorOrgRemoveDescription": "Ocorreu um erro ao remover o utilizador.", "userOrgRemoved": "Usuário removido", - "userOrgRemovedDescription": "O usuário {email} foi removido da organização.", + "userOrgRemovedDescription": "O utilizador {email} foi removido da organização.", "userQuestionOrgRemove": "Tem certeza que deseja remover {email} da organização?", - "userMessageOrgRemove": "Uma vez removido, este usuário não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", - "userMessageOrgConfirm": "Para confirmar, digite o nome do usuário abaixo.", + "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", + "userMessageOrgConfirm": "Para confirmar, digite o nome do utilizador abaixo.", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrg": "Remover Usuário da Organização", - "users": "Usuários", + "users": "Utilizadores", "accessRoleMember": "Membro", "accessRoleOwner": "Proprietário", "userConfirmed": "Confirmado", @@ -411,7 +411,7 @@ "emailInvalid": "Endereço de email inválido", "inviteValidityDuration": "Por favor, selecione uma duração", "accessRoleSelectPlease": "Por favor, selecione uma função", - "usernameRequired": "Nome de usuário é obrigatório", + "usernameRequired": "Nome de utilizador é obrigatório", "idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "accessRoleErrorFetch": "Falha ao buscar funções", @@ -419,51 +419,51 @@ "idpErrorFetch": "Falha ao buscar provedores de identidade", "idpErrorFetchDescription": "Ocorreu um erro ao buscar provedores de identidade", "userErrorExists": "Usuário já existe", - "userErrorExistsDescription": "Este usuário já é membro da organização.", - "inviteError": "Falha ao convidar usuário", - "inviteErrorDescription": "Ocorreu um erro ao convidar o usuário", + "userErrorExistsDescription": "Este utilizador já é membro da organização.", + "inviteError": "Falha ao convidar utilizador", + "inviteErrorDescription": "Ocorreu um erro ao convidar o utilizador", "userInvited": "Usuário convidado", - "userInvitedDescription": "O usuário foi convidado com sucesso.", - "userErrorCreate": "Falha ao criar usuário", - "userErrorCreateDescription": "Ocorreu um erro ao criar o usuário", + "userInvitedDescription": "O utilizador foi convidado com sucesso.", + "userErrorCreate": "Falha ao criar utilizador", + "userErrorCreateDescription": "Ocorreu um erro ao criar o utilizador", "userCreated": "Usuário criado", - "userCreatedDescription": "O usuário foi criado com sucesso.", + "userCreatedDescription": "O utilizador foi criado com sucesso.", "userTypeInternal": "Usuário Interno", - "userTypeInternalDescription": "Convidar um usuário para se juntar à sua organização diretamente.", + "userTypeInternalDescription": "Convidar um utilizador para se juntar à sua organização diretamente.", "userTypeExternal": "Usuário Externo", - "userTypeExternalDescription": "Criar um usuário com um provedor de identidade externo.", - "accessUserCreateDescription": "Siga os passos abaixo para criar um novo usuário", - "userSeeAll": "Ver Todos os Usuários", + "userTypeExternalDescription": "Criar um utilizador com um provedor de identidade externo.", + "accessUserCreateDescription": "Siga os passos abaixo para criar um novo utilizador", + "userSeeAll": "Ver Todos os Utilizadores", "userTypeTitle": "Tipo de Usuário", - "userTypeDescription": "Determine como você deseja criar o usuário", + "userTypeDescription": "Determine como você deseja criar o utilizador", "userSettings": "Informações do Usuário", - "userSettingsDescription": "Insira os detalhes para o novo usuário", - "inviteEmailSent": "Enviar e-mail de convite para o usuário", + "userSettingsDescription": "Insira os detalhes para o novo utilizador", + "inviteEmailSent": "Enviar e-mail de convite para o utilizador", "inviteValid": "Válido Por", "selectDuration": "Selecionar duração", "accessRoleSelect": "Selecionar função", - "inviteEmailSentDescription": "Um e-mail foi enviado ao usuário com o link de acesso abaixo. Eles devem acessar o link para aceitar o convite.", - "inviteSentDescription": "O usuário foi convidado. Eles devem acessar o link abaixo para aceitar o convite.", + "inviteEmailSentDescription": "Um e-mail foi enviado ao utilizador com o link de acesso abaixo. Eles devem aceder ao link para aceitar o convite.", + "inviteSentDescription": "O utilizador foi convidado. Eles devem aceder ao link abaixo para aceitar o convite.", "inviteExpiresIn": "O convite expirará em {days, plural, one {# dia} other {# dias}}.", "idpTitle": "Informações Gerais", - "idpSelect": "Selecione o provedor de identidade para o usuário externo", - "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar usuários externos.", - "usernameUniq": "Isto deve corresponder ao nome de usuário único que existe no provedor de identidade selecionado.", + "idpSelect": "Selecione o provedor de identidade para o utilizador externo", + "idpNotConfigured": "Nenhum provedor de identidade está configurado. Configure um provedor de identidade antes de criar utilizadores externos.", + "usernameUniq": "Isto deve corresponder ao nome de utilizador único que existe no provedor de identidade selecionado.", "emailOptional": "E-mail (Opcional)", "nameOptional": "Nome (Opcional)", - "accessControls": "Controles de Acesso", - "userDescription2": "Gerenciar as configurações deste usuário", - "accessRoleErrorAdd": "Falha ao adicionar usuário à função", - "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar usuário à função.", + "accessControls": "Controlos de Acesso", + "userDescription2": "Gerir as configurações deste utilizador", + "accessRoleErrorAdd": "Falha ao adicionar utilizador à função", + "accessRoleErrorAddDescription": "Ocorreu um erro ao adicionar utilizador à função.", "userSaved": "Usuário salvo", - "userSavedDescription": "O usuário foi atualizado.", + "userSavedDescription": "O utilizador foi atualizado.", "autoProvisioned": "Auto provisionado", - "autoProvisionedDescription": "Permitir que este usuário seja gerenciado automaticamente pelo provedor de identidade", - "accessControlsDescription": "Gerencie o que este usuário pode acessar e fazer na organização", - "accessControlsSubmit": "Salvar Controles de Acesso", + "autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade", + "accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização", + "accessControlsSubmit": "Guardar Controlos de Acesso", "roles": "Funções", - "accessUsersRoles": "Gerenciar Usuários e Funções", - "accessUsersRolesDescription": "Convide usuários e adicione-os a funções para gerenciar o acesso à sua organização", + "accessUsersRoles": "Gerir Utilizadores e Funções", + "accessUsersRolesDescription": "Convide utilizadores e adicione-os a funções para gerir o acesso à sua organização", "key": "Chave", "createdAt": "Criado Em", "proxyErrorInvalidHeader": "Valor do cabeçalho Host personalizado inválido. Use o formato de nome de domínio ou salve vazio para remover o cabeçalho Host personalizado.", @@ -497,7 +497,7 @@ "targetTlsSettingsAdvanced": "Configurações TLS Avançadas", "targetTlsSni": "Nome do Servidor TLS (SNI)", "targetTlsSniDescription": "O Nome do Servidor TLS para usar para SNI. Deixe vazio para usar o padrão.", - "targetTlsSubmit": "Salvar Configurações", + "targetTlsSubmit": "Guardar Configurações", "targets": "Configuração de Alvos", "targetsDescription": "Configure alvos para rotear tráfego para seus serviços de backend", "targetStickySessions": "Ativar Sessões Persistentes", @@ -506,12 +506,12 @@ "targetSubmit": "Adicionar Alvo", "targetNoOne": "Sem alvos. Adicione um alvo usando o formulário.", "targetNoOneDescription": "Adicionar mais de um alvo acima habilitará o balanceamento de carga.", - "targetsSubmit": "Salvar Alvos", + "targetsSubmit": "Guardar Alvos", "proxyAdditional": "Configurações Adicionais de Proxy", "proxyAdditionalDescription": "Configure como seu recurso lida com configurações de proxy", "proxyCustomHeader": "Cabeçalho Host Personalizado", "proxyCustomHeaderDescription": "O cabeçalho host para definir ao fazer proxy de requisições. Deixe vazio para usar o padrão.", - "proxyAdditionalSubmit": "Salvar Configurações de Proxy", + "proxyAdditionalSubmit": "Guardar Configurações de Proxy", "subnetMaskErrorInvalid": "Máscara de subnet inválida. Deve estar entre 0 e 32.", "ipAddressErrorInvalidFormat": "Formato de endereço IP inválido", "ipAddressErrorInvalidOctet": "Octeto de endereço IP inválido", @@ -564,7 +564,7 @@ "ruleSubmit": "Adicionar Regra", "rulesNoOne": "Sem regras. Adicione uma regra usando o formulário.", "rulesOrder": "As regras são avaliadas por prioridade em ordem ascendente.", - "rulesSubmit": "Salvar Regras", + "rulesSubmit": "Guardar Regras", "resourceErrorCreate": "Erro ao criar recurso", "resourceErrorCreateDescription": "Ocorreu um erro ao criar o recurso", "resourceErrorCreateMessage": "Erro ao criar recurso:", @@ -579,7 +579,7 @@ "resourcesDescription": "Recursos são proxies para aplicações executando em sua rede privada. Crie um recurso para qualquer serviço HTTP/HTTPS ou TCP/UDP bruto em sua rede privada. Cada recurso deve estar conectado a um site para habilitar conectividade privada e segura através de um túnel WireGuard criptografado.", "resourcesWireGuardConnect": "Conectividade segura com criptografia WireGuard", "resourcesMultipleAuthenticationMethods": "Configure múltiplos métodos de autenticação", - "resourcesUsersRolesAccess": "Controle de acesso baseado em usuários e funções", + "resourcesUsersRolesAccess": "Controle de acesso baseado em utilizadores e funções", "resourcesErrorUpdate": "Falha ao alternar recurso", "resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso", "access": "Acesso", @@ -609,7 +609,7 @@ "pangolinSettings": "Configurações - Pangolin", "accessRoleYour": "Sua função:", "accessRoleSelect2": "Selecionar uma função", - "accessUserSelect": "Selecionar um usuário", + "accessUserSelect": "Selecionar um utilizador", "otpEmailEnter": "Digite um e-mail", "otpEmailEnterDescription": "Pressione enter para adicionar um e-mail após digitá-lo no campo de entrada.", "otpEmailErrorInvalid": "Endereço de e-mail inválido. O caractere curinga (*) deve ser a parte local inteira.", @@ -619,8 +619,8 @@ "otpEmailTitleDescription": "Requer autenticação baseada em e-mail para acesso ao recurso", "otpEmailWhitelist": "Lista de E-mails Permitidos", "otpEmailWhitelistList": "E-mails na Lista Permitida", - "otpEmailWhitelistListDescription": "Apenas usuários com estes endereços de e-mail poderão acessar este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", - "otpEmailWhitelistSave": "Salvar Lista Permitida", + "otpEmailWhitelistListDescription": "Apenas utilizadores com estes endereços de e-mail poderão aceder este recurso. Eles serão solicitados a inserir uma senha única enviada para seu e-mail. Caracteres curinga (*@example.com) podem ser usados para permitir qualquer endereço de e-mail de um domínio.", + "otpEmailWhitelistSave": "Guardar Lista Permitida", "passwordAdd": "Adicionar Senha", "passwordRemove": "Remover Senha", "pincodeAdd": "Adicionar Código PIN", @@ -660,14 +660,14 @@ "resourcePincodeSetupDescription": "O código PIN do recurso foi definido com sucesso", "resourcePincodeSetupTitle": "Definir Código PIN", "resourcePincodeSetupTitleDescription": "Defina um código PIN para proteger este recurso", - "resourceRoleDescription": "Administradores sempre podem acessar este recurso.", - "resourceUsersRoles": "Usuários e Funções", - "resourceUsersRolesDescription": "Configure quais usuários e funções podem visitar este recurso", - "resourceUsersRolesSubmit": "Salvar Usuários e Funções", + "resourceRoleDescription": "Administradores sempre podem aceder este recurso.", + "resourceUsersRoles": "Utilizadores e Funções", + "resourceUsersRolesDescription": "Configure quais utilizadores e funções podem visitar este recurso", + "resourceUsersRolesSubmit": "Guardar Utilizadores e Funções", "resourceWhitelistSave": "Salvo com sucesso", "resourceWhitelistSaveDescription": "As configurações da lista permitida foram salvas", "ssoUse": "Usar SSO da Plataforma", - "ssoUseDescription": "Os usuários existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", + "ssoUseDescription": "Os utilizadores existentes só precisarão fazer login uma vez para todos os recursos que tiverem isso habilitado.", "proxyErrorInvalidPort": "Número da porta inválido", "subdomainErrorInvalid": "Subdomínio inválido", "domainErrorFetch": "Erro ao buscar domínios", @@ -693,7 +693,7 @@ "siteDestination": "Site de Destino", "searchSites": "Pesquisar sites", "accessRoleCreate": "Criar Função", - "accessRoleCreateDescription": "Crie uma nova função para agrupar usuários e gerenciar suas permissões.", + "accessRoleCreateDescription": "Crie uma nova função para agrupar utilizadores e gerir suas permissões.", "accessRoleCreateSubmit": "Criar Função", "accessRoleCreated": "Função criada", "accessRoleCreatedDescription": "A função foi criada com sucesso.", @@ -703,13 +703,13 @@ "accessRoleErrorRemove": "Falha ao remover função", "accessRoleErrorRemoveDescription": "Ocorreu um erro ao remover a função.", "accessRoleName": "Nome da Função", - "accessRoleQuestionRemove": "Você está prestes a excluir a função {name}. Você não pode desfazer esta ação.", + "accessRoleQuestionRemove": "Você está prestes a apagar a função {name}. Você não pode desfazer esta ação.", "accessRoleRemove": "Remover Função", "accessRoleRemoveDescription": "Remover uma função da organização", "accessRoleRemoveSubmit": "Remover Função", "accessRoleRemoved": "Função removida", "accessRoleRemovedDescription": "A função foi removida com sucesso.", - "accessRoleRequiredRemove": "Antes de excluir esta função, selecione uma nova função para transferir os membros existentes.", + "accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.", "manage": "Gerir", "sitesNotFound": "Nenhum site encontrado.", "pangolinServerAdmin": "Administrador do Servidor - Pangolin", @@ -919,8 +919,8 @@ "idpErrorNotFound": "IdP não encontrado", "inviteInvalid": "Convite Inválido", "inviteInvalidDescription": "O link do convite é inválido.", - "inviteErrorWrongUser": "O convite não é para este usuário", - "inviteErrorUserNotExists": "O usuário não existe. Por favor, crie uma conta primeiro.", + "inviteErrorWrongUser": "O convite não é para este utilizador", + "inviteErrorUserNotExists": "O utilizador não existe. Por favor, crie uma conta primeiro.", "inviteErrorLoginRequired": "Você deve estar logado para aceitar um convite", "inviteErrorExpired": "O convite pode ter expirado", "inviteErrorRevoked": "O convite pode ter sido revogado", @@ -935,7 +935,7 @@ "home": "Início", "accessControl": "Controle de Acesso", "settings": "Configurações", - "usersAll": "Todos os Usuários", + "usersAll": "Todos os Utilizadores", "license": "Licença", "pangolinDashboard": "Painel - Pangolin", "noResults": "Nenhum resultado encontrado.", @@ -988,8 +988,8 @@ "licenseTierProfessionalRequired": "Edição Profissional Necessária", "licenseTierProfessionalRequiredDescription": "Esta funcionalidade só está disponível na Edição Profissional.", "actionGetOrg": "Obter Organização", - "updateOrgUser": "Atualizar usuário Org", - "createOrgUser": "Criar usuário Org", + "updateOrgUser": "Atualizar utilizador Org", + "createOrgUser": "Criar utilizador Org", "actionUpdateOrg": "Atualizar Organização", "actionUpdateUser": "Atualizar Usuário", "actionGetUser": "Obter Usuário", @@ -1136,8 +1136,8 @@ "sidebarRoles": "Papéis", "sidebarShareableLinks": "Links compartilháveis", "sidebarApiKeys": "Chaves API", - "sidebarSettings": "Confirgurações", - "sidebarAllUsers": "Todos os usuários", + "sidebarSettings": "Configurações", + "sidebarAllUsers": "Todos os utilizadores", "sidebarIdentityProviders": "Provedores de identidade", "sidebarLicense": "Tipo:", "sidebarClients": "Clientes (Beta)", @@ -1190,7 +1190,7 @@ "loading": "Carregando", "restart": "Reiniciar", "domains": "Domínios", - "domainsDescription": "Gerencie domínios para sua organização", + "domainsDescription": "Gerir domínios para sua organização", "domainsSearch": "Pesquisar domínios...", "domainAdd": "Adicionar Domínio", "domainAddDescription": "Registre um novo domínio com sua organização", @@ -1218,7 +1218,7 @@ "pending": "Pendente", "sidebarBilling": "Faturamento", "billing": "Faturamento", - "orgBillingDescription": "Gerencie suas informações de faturamento e assinaturas", + "orgBillingDescription": "Gerir suas informações de faturação e assinaturas", "github": "GitHub", "pangolinHosted": "Hospedagem Pangolin", "fossorial": "Fossorial", @@ -1233,7 +1233,7 @@ "completeSetup": "Configuração Completa", "accountSetupSuccess": "Configuração da conta concluída! Bem-vindo ao Pangolin!", "documentation": "Documentação", - "saveAllSettings": "Salvar Todas as Configurações", + "saveAllSettings": "Guardar Todas as Configurações", "settingsUpdated": "Configurações atualizadas", "settingsUpdatedDescription": "Todas as configurações foram atualizadas com sucesso", "settingsErrorUpdate": "Falha ao atualizar configurações", @@ -1258,55 +1258,55 @@ "domainPickerSubdomain": "Subdomínio: {subdomain}", "domainPickerNamespace": "Namespace: {namespace}", "domainPickerShowMore": "Mostrar Mais", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Selecionar Região", + "regionSelectorInfo": "Selecionar uma região nos ajuda a fornecer melhor desempenho para sua localização. Você não precisa estar na mesma região que seu servidor.", + "regionSelectorPlaceholder": "Escolher uma região", + "regionSelectorComingSoon": "Em breve", + "billingLoadingSubscription": "Carregando assinatura...", + "billingFreeTier": "Plano Gratuito", + "billingWarningOverLimit": "Aviso: Você ultrapassou um ou mais limites de uso. Seus sites não se conectarão até você modificar sua assinatura ou ajustar seu uso.", + "billingUsageLimitsOverview": "Visão Geral dos Limites de Uso", + "billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@fossorial.io.", + "billingDataUsage": "Uso de Dados", + "billingOnlineTime": "Tempo Online do Site", + "billingUsers": "Usuários Ativos", + "billingDomains": "Domínios Ativos", + "billingRemoteExitNodes": "Nodos Auto-Hospedados Ativos", + "billingNoLimitConfigured": "Nenhum limite configurado", + "billingEstimatedPeriod": "Período Estimado de Cobrança", + "billingIncludedUsage": "Uso Incluído", + "billingIncludedUsageDescription": "Uso incluído no seu plano de assinatura atual", + "billingFreeTierIncludedUsage": "Limites de uso do plano gratuito", + "billingIncluded": "incluído", + "billingEstimatedTotal": "Total Estimado:", + "billingNotes": "Notas", + "billingEstimateNote": "Esta é uma estimativa baseada no seu uso atual.", + "billingActualChargesMayVary": "As cobranças reais podem variar.", + "billingBilledAtEnd": "Sua cobrança será feita ao final do período de cobrança.", + "billingModifySubscription": "Modificar Assinatura", + "billingStartSubscription": "Iniciar Assinatura", + "billingRecurringCharge": "Cobrança Recorrente", + "billingManageSubscriptionSettings": "Gerenciar as configurações e preferências da sua assinatura", + "billingNoActiveSubscription": "Você não tem uma assinatura ativa. Inicie sua assinatura para aumentar os limites de uso.", + "billingFailedToLoadSubscription": "Falha ao carregar assinatura", + "billingFailedToLoadUsage": "Falha ao carregar uso", + "billingFailedToGetCheckoutUrl": "Falha ao obter URL de checkout", + "billingPleaseTryAgainLater": "Por favor, tente novamente mais tarde.", + "billingCheckoutError": "Erro de Checkout", + "billingFailedToGetPortalUrl": "Falha ao obter URL do portal", + "billingPortalError": "Erro do Portal", + "billingDataUsageInfo": "Você é cobrado por todos os dados transferidos através de seus túneis seguros quando conectado à nuvem. Isso inclui o tráfego de entrada e saída em todos os seus sites. Quando você atingir o seu limite, seus sites desconectarão até que você atualize seu plano ou reduza o uso. Os dados não serão cobrados ao usar os nós.", + "billingOnlineTimeInfo": "Cobrança de acordo com o tempo em que seus sites permanecem conectados à nuvem. Por exemplo, 44,640 minutos é igual a um site que roda 24/7 para um mês inteiro. Quando você atinge o seu limite, seus sites desconectarão até que você faça o upgrade do seu plano ou reduza o uso. O tempo não é cobrado ao usar nós.", + "billingUsersInfo": "Você será cobrado por cada usuário em sua organização. A cobrança é calculada diariamente com base no número de contas de usuário ativas em sua organização.", + "billingDomainInfo": "Você será cobrado por cada domínio em sua organização. A cobrança é calculada diariamente com base no número de contas de domínio ativas em sua organização.", + "billingRemoteExitNodesInfo": "Você será cobrado por cada Nodo gerenciado em sua organização. A cobrança é calculada diariamente com base no número de Nodos gerenciados ativos em sua organização.", "domainNotFound": "Domínio Não Encontrado", "domainNotFoundDescription": "Este recurso está desativado porque o domínio não existe mais em nosso sistema. Defina um novo domínio para este recurso.", "failed": "Falhou", "createNewOrgDescription": "Crie uma nova organização", "organization": "Organização", "port": "Porta", - "securityKeyManage": "Gerenciar chaves de segurança", + "securityKeyManage": "Gerir chaves de segurança", "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", "securityKeyRegister": "Registrar nova chave de segurança", "securityKeyList": "Suas chaves de segurança", @@ -1357,13 +1357,13 @@ "createDomainARecords": "Registros A", "createDomainRecordNumber": "Registrar {number}", "createDomainTxtRecords": "Registros TXT", - "createDomainSaveTheseRecords": "Salvar Esses Registros", + "createDomainSaveTheseRecords": "Guardar Esses Registros", "createDomainSaveTheseRecordsDescription": "Certifique-se de salvar esses registros DNS, pois você não os verá novamente.", "createDomainDnsPropagation": "Propagação DNS", "createDomainDnsPropagationDescription": "Alterações no DNS podem levar algum tempo para se propagar pela internet. Pode levar de alguns minutos a 48 horas, dependendo do seu provedor de DNS e das configurações de TTL.", "resourcePortRequired": "Número da porta é obrigatório para recursos não-HTTP", "resourcePortNotAllowed": "Número da porta não deve ser definido para recursos HTTP", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Calculadora de Preços", "signUpTerms": { "IAgreeToThe": "Concordo com", "termsOfService": "os termos de serviço", @@ -1412,41 +1412,41 @@ "addNewTarget": "Adicionar Novo Alvo", "targetsList": "Lista de Alvos", "targetErrorDuplicateTargetFound": "Alvo duplicado encontrado", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Saudável", + "healthCheckUnhealthy": "Não Saudável", + "healthCheckUnknown": "Desconhecido", + "healthCheck": "Verificação de Saúde", + "configureHealthCheck": "Configurar Verificação de Saúde", + "configureHealthCheckDescription": "Configure a monitorização de saúde para {target}", + "enableHealthChecks": "Ativar Verificações de Saúde", + "enableHealthChecksDescription": "Monitore a saúde deste alvo. Você pode monitorar um ponto de extremidade diferente do alvo, se necessário.", + "healthScheme": "Método", + "healthSelectScheme": "Selecione o Método", + "healthCheckPath": "Caminho", + "healthHostname": "IP / Nome do Host", + "healthPort": "Porta", + "healthCheckPathDescription": "O caminho para verificar o estado de saúde.", + "healthyIntervalSeconds": "Intervalo Saudável", + "unhealthyIntervalSeconds": "Intervalo Não Saudável", + "IntervalSeconds": "Intervalo Saudável", + "timeoutSeconds": "Tempo Limite", + "timeIsInSeconds": "O tempo está em segundos", + "retryAttempts": "Tentativas de Repetição", + "expectedResponseCodes": "Códigos de Resposta Esperados", + "expectedResponseCodesDescription": "Código de status HTTP que indica estado saudável. Se deixado em branco, 200-300 é considerado saudável.", "customHeaders": "Cabeçalhos Personalizados", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Separados por cabeçalhos da nova linha: Nome do Cabeçalho: valor", + "headersValidationError": "Cabeçalhos devem estar no formato: Nome do Cabeçalho: valor.", + "saveHealthCheck": "Salvar Verificação de Saúde", + "healthCheckSaved": "Verificação de Saúde Salva", + "healthCheckSavedDescription": "Configuração de verificação de saúde salva com sucesso", + "healthCheckError": "Erro de Verificação de Saúde", + "healthCheckErrorDescription": "Ocorreu um erro ao salvar a configuração de verificação de saúde", + "healthCheckPathRequired": "O caminho de verificação de saúde é obrigatório", + "healthCheckMethodRequired": "O método HTTP é obrigatório", + "healthCheckIntervalMin": "O intervalo de verificação deve ser de pelo menos 5 segundos", + "healthCheckTimeoutMin": "O tempo limite deve ser de pelo menos 1 segundo", + "healthCheckRetryMin": "As tentativas de repetição devem ser pelo menos 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Selecionar método HTTP", "domainPickerSubdomainLabel": "Subdomínio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Digite um subdomínio para buscar e selecionar entre os domínios gratuitos disponíveis.", "domainPickerFreeDomains": "Domínios Gratuitos", "domainPickerSearchForAvailableDomains": "Pesquise por domínios disponíveis", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Nota: Domínios gratuitos fornecidos não estão disponíveis para instâncias auto-hospedadas no momento.", "resourceDomain": "Domínio", "resourceEditDomain": "Editar Domínio", "siteName": "Nome do Site", @@ -1481,7 +1481,7 @@ "editInternalResourceDialogSitePort": "Porta do Site", "editInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "editInternalResourceDialogCancel": "Cancelar", - "editInternalResourceDialogSaveResource": "Salvar Recurso", + "editInternalResourceDialogSaveResource": "Guardar Recurso", "editInternalResourceDialogSuccess": "Sucesso", "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno atualizado com sucesso", "editInternalResourceDialogError": "Erro", @@ -1508,7 +1508,7 @@ "createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogUdp": "UDP", "createInternalResourceDialogSitePort": "Porta do Site", - "createInternalResourceDialogSitePortDescription": "Use esta porta para acessar o recurso no site quando conectado com um cliente.", + "createInternalResourceDialogSitePortDescription": "Use esta porta para aceder o recurso no site quando conectado com um cliente.", "createInternalResourceDialogTargetConfiguration": "Configuração do Alvo", "createInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "createInternalResourceDialogDestinationPortDescription": "A porta no IP de destino onde o recurso está acessível.", @@ -1532,7 +1532,7 @@ "siteAddress": "Endereço do Site", "siteAddressDescription": "Especificar o endereço IP do host para que os clientes se conectem. Este é o endereço interno do site na rede Pangolin para os clientes endereçarem. Deve estar dentro da sub-rede da Organização.", "autoLoginExternalIdp": "Login Automático com IDP Externo", - "autoLoginExternalIdpDescription": "Redirecionar imediatamente o usuário para o IDP externo para autenticação.", + "autoLoginExternalIdpDescription": "Redirecionar imediatamente o utilizador para o IDP externo para autenticação.", "selectIdp": "Selecionar IDP", "selectIdpPlaceholder": "Escolher um IDP...", "selectIdpRequired": "Por favor, selecione um IDP quando o login automático estiver ativado.", @@ -1543,72 +1543,72 @@ "autoLoginError": "Erro de Login Automático", "autoLoginErrorNoRedirectUrl": "Nenhum URL de redirecionamento recebido do provedor de identidade.", "autoLoginErrorGeneratingUrl": "Falha ao gerar URL de autenticação.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Gerenciar Auto-Hospedados", + "remoteExitNodeDescription": "Gerencie os nós para estender sua conectividade de rede", + "remoteExitNodes": "Nós", + "searchRemoteExitNodes": "Buscar nós...", + "remoteExitNodeAdd": "Adicionar node", + "remoteExitNodeErrorDelete": "Erro ao excluir nó", + "remoteExitNodeQuestionRemove": "Tem certeza que deseja remover o nó {selectedNode} da organização?", + "remoteExitNodeMessageRemove": "Uma vez removido, o nó não estará mais acessível.", + "remoteExitNodeMessageConfirm": "Para confirmar, por favor, digite o nome do nó abaixo.", + "remoteExitNodeConfirmDelete": "Confirmar exclusão do nó", + "remoteExitNodeDelete": "Excluir nó", + "sidebarRemoteExitNodes": "Nós", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Criar nó", + "description": "Crie um novo nó para estender sua conectividade de rede", + "viewAllButton": "Ver Todos os Nós", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Estratégia de Criação", + "description": "Escolha isto para configurar o seu nó manualmente ou gerar novas credenciais.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adotar Nodo", + "description": "Escolha isto se você já tem credenciais para o nó." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Gerar Chaves", + "description": "Escolha esta opção se você quer gerar novas chaves para o nó" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Adotar Nodo Existente", + "description": "Digite as credenciais do nó existente que deseja adoptar", + "nodeIdLabel": "Nó ID", + "nodeIdDescription": "O ID do nó existente que você deseja adoptar", + "secretLabel": "Chave Secreta", + "secretDescription": "A chave secreta do nó existente", + "submitButton": "Nó Adotado" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Credenciais Geradas", + "description": "Use estas credenciais geradas para configurar o seu nó", + "nodeIdTitle": "Nó ID", + "secretTitle": "Chave Secreta", + "saveCredentialsTitle": "Adicionar Credenciais à Configuração", + "saveCredentialsDescription": "Adicione essas credenciais ao arquivo de configuração do seu nodo de Pangolin auto-hospedado para completar a conexão.", + "submitButton": "Criar nó" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "ID do nó e Segredo são necessários ao adotar um nó existente" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Falha ao carregar padrões", + "defaultsNotLoaded": "Padrões não carregados", + "createFailed": "Falha ao criar nó" }, "success": { - "created": "Node created successfully" + "created": "Nó criado com sucesso" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Seleção de nó", + "remoteExitNodeSelectionDescription": "Selecione um nó para encaminhar o tráfego para este site local", + "remoteExitNodeRequired": "Um nó deve ser seleccionado para sites locais", + "noRemoteExitNodesAvailable": "Nenhum nó disponível", + "noRemoteExitNodesAvailableDescription": "Nenhum nó está disponível para esta organização. Crie um nó primeiro para usar sites locais.", + "exitNode": "Nodo de Saída", + "country": "País", + "rulesMatchCountry": "Atualmente baseado no IP de origem", "managedSelfHosted": { "title": "Gerenciado Auto-Hospedado", "description": "Servidor Pangolin auto-hospedado mais confiável e com baixa manutenção com sinos extras e assobiamentos", @@ -1625,7 +1625,7 @@ }, "benefitLessMaintenance": { "title": "Menos manutenção", - "description": "Sem migrações, backups ou infraestrutura extra para gerenciar. Lidamos com isso na nuvem." + "description": "Sem migrações, backups ou infraestrutura extra para gerir. Lidamos com isso na nuvem." }, "benefitCloudFailover": { "title": "Falha na nuvem", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Domínio Internacional Detectado", "willbestoredas": "Será armazenado como:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Determinar como as funções são atribuídas aos usuários quando eles fazem login quando Auto Provisão está habilitada.", + "selectRole": "Selecione uma função", + "roleMappingExpression": "Expressão", + "selectRolePlaceholder": "Escolha uma função", + "selectRoleDescription": "Selecione uma função para atribuir a todos os usuários deste provedor de identidade", + "roleMappingExpressionDescription": "Insira uma expressão JMESPath para extrair informações da função do token de ID", + "idpTenantIdRequired": "ID do inquilino é necessária", + "invalidValue": "Valor Inválido", + "idpTypeLabel": "Tipo de provedor de identidade", + "roleMappingExpressionPlaceholder": "ex.: Contem (grupos, 'administrador') && 'Administrador' 「'Membro'", + "idpGoogleConfiguration": "Configuração do Google", + "idpGoogleConfigurationDescription": "Configurar suas credenciais do Google OAuth2", + "idpGoogleClientIdDescription": "Seu ID de Cliente OAuth2 do Google", + "idpGoogleClientSecretDescription": "Seu Segredo de Cliente OAuth2 do Google", + "idpAzureConfiguration": "Configuração de ID do Azure Entra", + "idpAzureConfigurationDescription": "Configure as suas credenciais do Azure Entra ID OAuth2", + "idpTenantId": "ID do Inquilino", + "idpTenantIdPlaceholder": "seu-tenente-id", + "idpAzureTenantIdDescription": "Seu ID do tenant Azure (encontrado na visão geral do diretório ativo Azure)", + "idpAzureClientIdDescription": "Seu ID de Cliente de Registro do App Azure", + "idpAzureClientSecretDescription": "Seu segredo de cliente de registro de aplicativos Azure", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Configuração do Google", + "idpAzureConfigurationTitle": "Configuração de ID do Azure Entra", + "idpTenantIdLabel": "ID do Inquilino", + "idpAzureClientIdDescription2": "Seu ID de Cliente de Registro do App Azure", + "idpAzureClientSecretDescription2": "Seu segredo de cliente de registro de aplicativos Azure", "idpGoogleDescription": "Provedor Google OAuth2/OIDC", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Sub-rede", + "subnetDescription": "A sub-rede para a configuração de rede dessa organização.", + "authPage": "Página de Autenticação", + "authPageDescription": "Configurar a página de autenticação para sua organização", + "authPageDomain": "Domínio de Página Autenticação", + "noDomainSet": "Nenhum domínio definido", + "changeDomain": "Alterar domínio", + "selectDomain": "Selecionar domínio", + "restartCertificate": "Reiniciar Certificado", + "editAuthPageDomain": "Editar Página de Autenticação", + "setAuthPageDomain": "Definir domínio da página de autenticação", + "failedToFetchCertificate": "Falha ao buscar o certificado", + "failedToRestartCertificate": "Falha ao reiniciar o certificado", + "addDomainToEnableCustomAuthPages": "Adicione um domínio para habilitar páginas de autenticação personalizadas para sua organização", + "selectDomainForOrgAuthPage": "Selecione um domínio para a página de autenticação da organização", "domainPickerProvidedDomain": "Domínio fornecido", "domainPickerFreeProvidedDomain": "Domínio fornecido grátis", "domainPickerVerified": "Verificada", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" não pôde ser válido para {domain}.", "domainPickerSubdomainSanitized": "Subdomínio banalizado", "domainPickerSubdomainCorrected": "\"{sub}\" foi corrigido para \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Entrar na sua organização", + "orgAuthChooseIdpDescription": "Escolha o seu provedor de identidade para continuar", + "orgAuthNoIdpConfigured": "Esta organização não tem nenhum provedor de identidade configurado. Você pode entrar com a identidade do seu Pangolin.", + "orgAuthSignInWithPangolin": "Entrar com o Pangolin", + "subscriptionRequiredToUse": "Uma assinatura é necessária para usar esse recurso.", + "idpDisabled": "Provedores de identidade estão desabilitados.", + "orgAuthPageDisabled": "A página de autenticação da organização está desativada.", + "domainRestartedDescription": "Verificação de domínio reiniciado com sucesso", "resourceAddEntrypointsEditFile": "Editar arquivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar arquivo: docker-compose.yml", "emailVerificationRequired": "Verificação de e-mail é necessária. Por favor, faça login novamente via {dashboardUrl}/auth/login conclui esta etapa. Em seguida, volte aqui.", "twoFactorSetupRequired": "Configuração de autenticação de dois fatores é necessária. Por favor, entre novamente via {dashboardUrl}/auth/login conclua este passo. Em seguida, volte aqui.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Ocorreu um erro ao atualizar as configurações da página de autenticação", + "authPageUpdated": "Página de autenticação atualizada com sucesso", + "healthCheckNotAvailable": "Localização", + "rewritePath": "Reescrever Caminho", + "rewritePathDescription": "Opcionalmente reescreva o caminho antes de encaminhar ao destino." } From 87f1cf6730dc82b6b206eba28b43b14d4cc050a4 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:04 -0700 Subject: [PATCH 151/322] New translations en-us.json (Russian) --- messages/ru-RU.json | 374 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/messages/ru-RU.json b/messages/ru-RU.json index a2508d47..80be36dd 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Простейший способ создать точку входа в вашу сеть. Дополнительная настройка не требуется.", "siteWg": "Базовый WireGuard", "siteWgDescription": "Используйте любой клиент WireGuard для открытия туннеля. Требуется ручная настройка NAT.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Используйте любой клиент WireGuard для создания туннеля. Требуется ручная настройка NAT. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ", "siteLocalDescription": "Только локальные ресурсы. Без туннелирования.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Только локальные ресурсы. Без туннелирования. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ", "siteSeeAll": "Просмотреть все сайты", "siteTunnelDescription": "Выберите способ подключения к вашему сайту", "siteNewtCredentials": "Учётные данные Newt", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-ресурс", "resourceHTTPDescription": "Проксирование запросов к вашему приложению через HTTPS с использованием поддомена или базового домена.", "resourceRaw": "Сырой TCP/UDP-ресурс", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Проксирование запросов к вашему приложению через TCP/UDP с использованием по номеру порта.", "resourceCreate": "Создание ресурса", "resourceCreateDescription": "Следуйте инструкциям ниже для создания нового ресурса", "resourceSeeAll": "Посмотреть все ресурсы", @@ -168,9 +168,9 @@ "siteSelect": "Выберите сайт", "siteSearch": "Поиск сайта", "siteNotFound": "Сайт не найден.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Выберите страну", + "searchCountries": "Поиск стран...", + "noCountryFound": "Страна не найдена.", "siteSelectionDescription": "Этот сайт предоставит подключение к цели.", "resourceType": "Тип ресурса", "resourceTypeDescription": "Определите, как вы хотите получать доступ к вашему ресурсу", @@ -239,7 +239,7 @@ "accessUserCreate": "Создать пользователя", "accessUserRemove": "Удалить пользователя", "username": "Имя пользователя", - "identityProvider": "Identity Provider", + "identityProvider": "Поставщик удостоверений", "role": "Роль", "nameRequired": "Имя обязательно", "accessRolesManage": "Управление ролями", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Поддомен: {subdomain}", "domainPickerNamespace": "Пространство имен: {namespace}", "domainPickerShowMore": "Показать еще", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Выберите регион", + "regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.", + "regionSelectorPlaceholder": "Выбор региона", + "regionSelectorComingSoon": "Скоро будет", + "billingLoadingSubscription": "Загрузка подписки...", + "billingFreeTier": "Бесплатный уровень", + "billingWarningOverLimit": "Предупреждение: Вы превысили одну или несколько границ использования. Ваши сайты не подключатся, пока вы не измените подписку или не скорректируете использование.", + "billingUsageLimitsOverview": "Обзор лимитов использования", + "billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@fossorial.io.", + "billingDataUsage": "Использование данных", + "billingOnlineTime": "Время работы сайта", + "billingUsers": "Активные пользователи", + "billingDomains": "Активные домены", + "billingRemoteExitNodes": "Активные самоуправляемые узлы", + "billingNoLimitConfigured": "Лимит не установлен", + "billingEstimatedPeriod": "Предполагаемый период выставления счетов", + "billingIncludedUsage": "Включенное использование", + "billingIncludedUsageDescription": "Использование, включенное в ваш текущий план подписки", + "billingFreeTierIncludedUsage": "Бесплатное использование ограничений", + "billingIncluded": "включено", + "billingEstimatedTotal": "Предполагаемая сумма:", + "billingNotes": "Заметки", + "billingEstimateNote": "Это приблизительная оценка на основании вашего текущего использования.", + "billingActualChargesMayVary": "Фактические начисления могут отличаться.", + "billingBilledAtEnd": "С вас будет выставлен счет в конце периода выставления счетов.", + "billingModifySubscription": "Изменить подписку", + "billingStartSubscription": "Начать подписку", + "billingRecurringCharge": "Периодический взнос", + "billingManageSubscriptionSettings": "Управляйте настройками и предпочтениями вашей подписки", + "billingNoActiveSubscription": "У вас нет активной подписки. Начните подписку, чтобы увеличить лимиты использования.", + "billingFailedToLoadSubscription": "Не удалось загрузить подписку", + "billingFailedToLoadUsage": "Не удалось загрузить использование", + "billingFailedToGetCheckoutUrl": "Не удалось получить URL-адрес для оплаты", + "billingPleaseTryAgainLater": "Пожалуйста, повторите попытку позже.", + "billingCheckoutError": "Ошибка при оформлении заказа", + "billingFailedToGetPortalUrl": "Не удалось получить URL-адрес портала", + "billingPortalError": "Ошибка портала", + "billingDataUsageInfo": "Вы несете ответственность за все данные, переданные через безопасные туннели при подключении к облаку. Это включает как входящий, так и исходящий трафик на всех ваших сайтах. При достижении лимита ваши сайты будут отключаться до тех пор, пока вы не обновите план или не уменьшите его использование. При использовании узлов не взимается плата.", + "billingOnlineTimeInfo": "Вы тарифицируете на то, как долго ваши сайты будут подключены к облаку. Например, 44 640 минут равны одному сайту, работающему круглосуточно за весь месяц. Когда вы достигните лимита, ваши сайты будут отключаться до тех пор, пока вы не обновите тарифный план или не сократите нагрузку. При использовании узлов не тарифицируется.", + "billingUsersInfo": "С вас взимается плата за каждого пользователя в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей пользователей в вашей организации.", + "billingDomainInfo": "С вас взимается плата за каждый домен в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей доменов в вашей организации.", + "billingRemoteExitNodesInfo": "С вас взимается плата за каждый управляемый узел в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных управляемых узлов в вашей организации.", "domainNotFound": "Домен не найден", "domainNotFoundDescription": "Этот ресурс отключен, так как домен больше не существует в нашей системе. Пожалуйста, установите новый домен для этого ресурса.", "failed": "Ошибка", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Изменения DNS могут занять некоторое время для распространения через интернет. Это может занять от нескольких минут до 48 часов в зависимости от вашего DNS провайдера и настроек TTL.", "resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов", "resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Калькулятор расценок", "signUpTerms": { "IAgreeToThe": "Я согласен с", "termsOfService": "условия использования", @@ -1412,41 +1412,41 @@ "addNewTarget": "Добавить новую цель", "targetsList": "Список целей", "targetErrorDuplicateTargetFound": "Обнаружена дублирующаяся цель", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Здоровый", + "healthCheckUnhealthy": "Нездоровый", + "healthCheckUnknown": "Неизвестно", + "healthCheck": "Проверка здоровья", + "configureHealthCheck": "Настроить проверку здоровья", + "configureHealthCheckDescription": "Настройте мониторинг состояния для {target}", + "enableHealthChecks": "Включить проверки здоровья", + "enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.", + "healthScheme": "Метод", + "healthSelectScheme": "Выберите метод", + "healthCheckPath": "Путь", + "healthHostname": "IP / хост", + "healthPort": "Порт", + "healthCheckPathDescription": "Путь к проверке состояния здоровья.", + "healthyIntervalSeconds": "Интервал здоровых состояний", + "unhealthyIntervalSeconds": "Интервал нездоровых состояний", + "IntervalSeconds": "Интервал здоровых состояний", + "timeoutSeconds": "Тайм-аут", + "timeIsInSeconds": "Время указано в секундах", + "retryAttempts": "Количество попыток повторного запроса", + "expectedResponseCodes": "Ожидаемые коды ответов", + "expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.", "customHeaders": "Пользовательские заголовки", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Заголовки новой строки, разделённые: название заголовка: значение", + "headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.", + "saveHealthCheck": "Сохранить проверку здоровья", + "healthCheckSaved": "Проверка здоровья сохранена", + "healthCheckSavedDescription": "Конфигурация проверки состояния успешно сохранена", + "healthCheckError": "Ошибка проверки состояния", + "healthCheckErrorDescription": "Произошла ошибка при сохранении конфигурации проверки состояния", + "healthCheckPathRequired": "Требуется путь проверки состояния", + "healthCheckMethodRequired": "Требуется метод HTTP", + "healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд", + "healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды", + "healthCheckRetryMin": "Количество попыток должно быть не менее 1", "httpMethod": "HTTP метод", "selectHttpMethod": "Выберите HTTP метод", "domainPickerSubdomainLabel": "Поддомен", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Введите поддомен для поиска и выбора из доступных свободных доменов.", "domainPickerFreeDomains": "Свободные домены", "domainPickerSearchForAvailableDomains": "Поиск доступных доменов", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Примечание: бесплатные предоставляемые домены в данный момент недоступны для самоуправляемых экземпляров.", "resourceDomain": "Домен", "resourceEditDomain": "Редактировать домен", "siteName": "Имя сайта", @@ -1543,72 +1543,72 @@ "autoLoginError": "Ошибка автоматического входа", "autoLoginErrorNoRedirectUrl": "URL-адрес перенаправления не получен от провайдера удостоверения.", "autoLoginErrorGeneratingUrl": "Не удалось сгенерировать URL-адрес аутентификации.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Управление самоуправляемым", + "remoteExitNodeDescription": "Управляйте узлами для расширения сетевого подключения", + "remoteExitNodes": "Узлы", + "searchRemoteExitNodes": "Поиск узлов...", + "remoteExitNodeAdd": "Добавить узел", + "remoteExitNodeErrorDelete": "Ошибка удаления узла", + "remoteExitNodeQuestionRemove": "Вы уверены, что хотите удалить узел {selectedNode} из организации?", + "remoteExitNodeMessageRemove": "После удаления узел больше не будет доступен.", + "remoteExitNodeMessageConfirm": "Для подтверждения введите имя узла ниже.", + "remoteExitNodeConfirmDelete": "Подтвердите удаление узла", + "remoteExitNodeDelete": "Удалить узел", + "sidebarRemoteExitNodes": "Узлы", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Создать узел", + "description": "Создайте новый узел, чтобы расширить сетевое подключение", + "viewAllButton": "Все узлы", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Стратегия создания", + "description": "Выберите эту опцию для настройки вашего узла или создания новых учетных данных.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Принять узел", + "description": "Выберите это, если у вас уже есть учетные данные для узла." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Сгенерировать ключи", + "description": "Выберите это, если вы хотите создать новые ключи для узла" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Принять существующий узел", + "description": "Введите учетные данные существующего узла, который вы хотите принять", + "nodeIdLabel": "ID узла", + "nodeIdDescription": "ID существующего узла, который вы хотите принять", + "secretLabel": "Секретный ключ", + "secretDescription": "Секретный ключ существующего узла", + "submitButton": "Принять узел" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Сгенерированные учетные данные", + "description": "Используйте эти учётные данные для настройки вашего узла", + "nodeIdTitle": "ID узла", + "secretTitle": "Секретный ключ", + "saveCredentialsTitle": "Добавить учетные данные в конфигурацию", + "saveCredentialsDescription": "Добавьте эти учетные данные в файл конфигурации вашего самоуправляемого узла Pangolin, чтобы завершить подключение.", + "submitButton": "Создать узел" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "ID узла и секрет требуются при установке существующего узла" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Не удалось загрузить параметры по умолчанию", + "defaultsNotLoaded": "Параметры по умолчанию не загружены", + "createFailed": "Не удалось создать узел" }, "success": { - "created": "Node created successfully" + "created": "Узел успешно создан" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Выбор узла", + "remoteExitNodeSelectionDescription": "Выберите узел для маршрутизации трафика для этого локального сайта", + "remoteExitNodeRequired": "Узел должен быть выбран для локальных сайтов", + "noRemoteExitNodesAvailable": "Нет доступных узлов", + "noRemoteExitNodesAvailableDescription": "Для этой организации узлы не доступны. Сначала создайте узел, чтобы использовать локальные сайты.", + "exitNode": "Узел выхода", + "country": "Страна", + "rulesMatchCountry": "В настоящее время основано на исходном IP", "managedSelfHosted": { "title": "Управляемый с самовывоза", "description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Обнаружен международный домен", "willbestoredas": "Будет храниться как:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Определите, как роли, назначаемые пользователям, когда они войдут в систему автоматического профиля.", + "selectRole": "Выберите роль", + "roleMappingExpression": "Выражение", + "selectRolePlaceholder": "Выберите роль", + "selectRoleDescription": "Выберите роль, чтобы назначить всем пользователям этого поставщика идентификации", + "roleMappingExpressionDescription": "Введите выражение JMESPath, чтобы извлечь информацию о роли из ID токена", + "idpTenantIdRequired": "Требуется ID владельца", + "invalidValue": "Неверное значение", + "idpTypeLabel": "Тип поставщика удостоверений", + "roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'", + "idpGoogleConfiguration": "Конфигурация Google", + "idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2", + "idpGoogleClientIdDescription": "Ваш Google OAuth2 ID клиента", + "idpGoogleClientSecretDescription": "Ваш Google OAuth2 Секрет", + "idpAzureConfiguration": "Конфигурация Azure Entra ID", + "idpAzureConfigurationDescription": "Настройте учетные данные Azure Entra ID OAuth2", + "idpTenantId": "Идентификатор арендатора", + "idpTenantIdPlaceholder": "ваш тенант-id", + "idpAzureTenantIdDescription": "Идентификатор арендатора Azure (найден в обзоре Active Directory Azure)", + "idpAzureClientIdDescription": "Ваш идентификатор клиента Azure App", + "idpAzureClientSecretDescription": "Секрет регистрации клиента Azure App", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Конфигурация Google", + "idpAzureConfigurationTitle": "Конфигурация Azure Entra ID", + "idpTenantIdLabel": "Идентификатор арендатора", + "idpAzureClientIdDescription2": "Ваш идентификатор клиента Azure App", + "idpAzureClientSecretDescription2": "Секрет регистрации клиента Azure App", "idpGoogleDescription": "Google OAuth2/OIDC провайдер", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Подсеть", + "subnetDescription": "Подсеть для конфигурации сети этой организации.", + "authPage": "Страница авторизации", + "authPageDescription": "Настройка страницы авторизации для вашей организации", + "authPageDomain": "Домен страницы авторизации", + "noDomainSet": "Домен не установлен", + "changeDomain": "Изменить домен", + "selectDomain": "Выберите домен", + "restartCertificate": "Перезапустить сертификат", + "editAuthPageDomain": "Редактировать домен страницы авторизации", + "setAuthPageDomain": "Установить домен страницы авторизации", + "failedToFetchCertificate": "Не удалось получить сертификат", + "failedToRestartCertificate": "Не удалось перезапустить сертификат", + "addDomainToEnableCustomAuthPages": "Добавьте домен для включения пользовательских страниц аутентификации для вашей организации", + "selectDomainForOrgAuthPage": "Выберите домен для страницы аутентификации организации", "domainPickerProvidedDomain": "Домен предоставлен", "domainPickerFreeProvidedDomain": "Бесплатный домен", "domainPickerVerified": "Подтверждено", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не может быть действительным для {domain}.", "domainPickerSubdomainSanitized": "Субдомен очищен", "domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Войдите в свою организацию", + "orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения", + "orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.", + "orgAuthSignInWithPangolin": "Войти через Pangolin", + "subscriptionRequiredToUse": "Для использования этой функции требуется подписка.", + "idpDisabled": "Провайдеры идентификации отключены.", + "orgAuthPageDisabled": "Страница авторизации организации отключена.", + "domainRestartedDescription": "Проверка домена успешно перезапущена", "resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml", "emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", "twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Произошла ошибка при обновлении настроек страницы авторизации", + "authPageUpdated": "Страница авторизации успешно обновлена", + "healthCheckNotAvailable": "Локальный", + "rewritePath": "Переписать путь", + "rewritePathDescription": "При необходимости, измените путь перед пересылкой к целевому адресу." } From 67ac01b31a931a6c76e6f3baaf9285400204561e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:05 -0700 Subject: [PATCH 152/322] New translations en-us.json (Turkish) --- messages/tr-TR.json | 376 ++++++++++++++++++++++---------------------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 62991e05..b84bef19 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -94,9 +94,9 @@ "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.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR", "siteLocalDescription": "Yalnızca yerel kaynaklar. Tünelleme yok.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Yalnızca yerel kaynaklar. Tünel yok. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR", "siteSeeAll": "Tüm Siteleri Gör", "siteTunnelDescription": "Sitenize nasıl bağlanmak istediğinizi belirleyin", "siteNewtCredentials": "Newt Kimlik Bilgileri", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS Kaynağı", "resourceHTTPDescription": "Bir alt alan adı veya temel alan adı kullanarak uygulamanıza HTTPS üzerinden vekil istek gönderin.", "resourceRaw": "Ham TCP/UDP Kaynağı", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Uygulamanıza TCP/UDP üzerinden port numarası ile vekil istek gönderin.", "resourceCreate": "Kaynak Oluştur", "resourceCreateDescription": "Yeni bir kaynak oluşturmak için aşağıdaki adımları izleyin", "resourceSeeAll": "Tüm Kaynakları Gör", @@ -168,9 +168,9 @@ "siteSelect": "Site seç", "siteSearch": "Site ara", "siteNotFound": "Herhangi bir site bulunamadı.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Ülke Seç", + "searchCountries": "Ülkeleri ara...", + "noCountryFound": "Ülke bulunamadı.", "siteSelectionDescription": "Bu site hedefe bağlantı sağlayacaktır.", "resourceType": "Kaynak Türü", "resourceTypeDescription": "Kaynağınıza nasıl erişmek istediğinizi belirleyin", @@ -817,7 +817,7 @@ "redirectUrl": "Yönlendirme URL'si", "redirectUrlAbout": "Yönlendirme URL'si Hakkında", "redirectUrlAboutDescription": "Bu, kimlik doğrulamasından sonra kullanıcıların yönlendirileceği URL'dir. Bu URL'yi kimlik sağlayıcınızın ayarlarında yapılandırmanız gerekir.", - "pangolinAuth": "Auth - Pangolin", + "pangolinAuth": "Yetkilendirme - Pangolin", "verificationCodeLengthRequirements": "Doğrulama kodunuz 8 karakter olmalıdır.", "errorOccurred": "Bir hata oluştu", "emailErrorVerify": "E-posta doğrulanamadı: ", @@ -1242,7 +1242,7 @@ "sidebarExpand": "Genişlet", "newtUpdateAvailable": "Güncelleme Mevcut", "newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.", - "domainPickerEnterDomain": "Domain", + "domainPickerEnterDomain": "Alan Adı", "domainPickerPlaceholder": "myapp.example.com", "domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.", "domainPickerDescriptionSaas": "Mevcut seçenekleri görmek için tam etki alanı, alt etki alanı veya sadece bir isim girin", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Alt Alan: {subdomain}", "domainPickerNamespace": "Ad Alanı: {namespace}", "domainPickerShowMore": "Daha Fazla Göster", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Bölge Seç", + "regionSelectorInfo": "Bir bölge seçmek, konumunuz için daha iyi performans sağlamamıza yardımcı olur. Sunucunuzla aynı bölgede olmanıza gerek yoktur.", + "regionSelectorPlaceholder": "Bölge Seçin", + "regionSelectorComingSoon": "Yakında Geliyor", + "billingLoadingSubscription": "Abonelik yükleniyor...", + "billingFreeTier": "Ücretsiz Dilim", + "billingWarningOverLimit": "Uyarı: Bir veya daha fazla kullanım limitini aştınız. Aboneliğinizi değiştirmediğiniz veya kullanımı ayarlamadığınız sürece siteleriniz bağlanmayacaktır.", + "billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü", + "billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@fossorial.io adresinden bizimle iletişime geçin.", + "billingDataUsage": "Veri Kullanımı", + "billingOnlineTime": "Site Çevrimiçi Süresi", + "billingUsers": "Aktif Kullanıcılar", + "billingDomains": "Aktif Alanlar", + "billingRemoteExitNodes": "Aktif Öz-Host Düğümleri", + "billingNoLimitConfigured": "Hiçbir limit yapılandırılmadı", + "billingEstimatedPeriod": "Tahmini Fatura Dönemi", + "billingIncludedUsage": "Dahil Kullanım", + "billingIncludedUsageDescription": "Mevcut abonelik planınıza bağlı kullanım", + "billingFreeTierIncludedUsage": "Ücretsiz dilim kullanım hakları", + "billingIncluded": "dahil", + "billingEstimatedTotal": "Tahmini Toplam:", + "billingNotes": "Notlar", + "billingEstimateNote": "Bu, mevcut kullanımınıza dayalı bir tahmindir.", + "billingActualChargesMayVary": "Asıl ücretler farklılık gösterebilir.", + "billingBilledAtEnd": "Fatura döneminin sonunda fatura düzenlenecektir.", + "billingModifySubscription": "Aboneliği Düzenle", + "billingStartSubscription": "Aboneliği Başlat", + "billingRecurringCharge": "Yinelenen Ücret", + "billingManageSubscriptionSettings": "Abonelik ayarlarınızı ve tercihlerinizi yönetin", + "billingNoActiveSubscription": "Aktif bir aboneliğiniz yok. Kullanım limitlerini artırmak için aboneliğinizi başlatın.", + "billingFailedToLoadSubscription": "Abonelik yüklenemedi", + "billingFailedToLoadUsage": "Kullanım yüklenemedi", + "billingFailedToGetCheckoutUrl": "Ödeme URL'si alınamadı", + "billingPleaseTryAgainLater": "Lütfen daha sonra tekrar deneyin.", + "billingCheckoutError": "Ödeme Hatası", + "billingFailedToGetPortalUrl": "Portal URL'si alınamadı", + "billingPortalError": "Portal Hatası", + "billingDataUsageInfo": "Buluta bağlandığınızda, güvenli tünellerinizden aktarılan tüm verilerden ücret alınırsınız. Bu, tüm sitelerinizdeki gelen ve giden trafiği içerir. Limitinize ulaştığınızda, planınızı yükseltmeli veya kullanımı azaltmalısınız, aksi takdirde siteleriniz bağlantıyı keser. Düğümler kullanırken verilerden ücret alınmaz.", + "billingOnlineTimeInfo": "Sitelerinizin buluta ne kadar süre bağlı kaldığına göre ücretlendirilirsiniz. Örneğin, 44,640 dakika, bir sitenin 24/7 boyunca tam bir ay boyunca çalışması anlamına gelir. Limitinize ulaştığınızda, planınızı yükseltmeyip kullanımı azaltmazsanız siteleriniz bağlantıyı keser. Düğümler kullanırken zamandan ücret alınmaz.", + "billingUsersInfo": "Kuruluşunuzdaki her kullanıcı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif kullanıcı hesaplarının sayısına göre günlük olarak hesaplanır.", + "billingDomainInfo": "Kuruluşunuzdaki her alan adı için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif alan adları hesaplarının sayısına göre günlük olarak hesaplanır.", + "billingRemoteExitNodesInfo": "Kuruluşunuzdaki her yönetilen Düğüm için ücretlendirilirsiniz. Faturalandırma, hesabınızdaki aktif yönetilen Düğümler sayısına göre günlük olarak hesaplanır.", "domainNotFound": "Alan Adı Bulunamadı", "domainNotFoundDescription": "Bu kaynak devre dışıdır çünkü alan adı sistemimizde artık mevcut değil. Bu kaynak için yeni bir alan adı belirleyin.", "failed": "Başarısız", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS değişikliklerinin internet genelinde yayılması zaman alabilir. DNS sağlayıcınız ve TTL ayarlarına bağlı olarak bu birkaç dakika ile 48 saat arasında değişebilir.", "resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir", "resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Fiyat Hesaplayıcı", "signUpTerms": { "IAgreeToThe": "Kabul ediyorum", "termsOfService": "hizmet şartları", @@ -1412,41 +1412,41 @@ "addNewTarget": "Yeni Hedef Ekle", "targetsList": "Hedefler Listesi", "targetErrorDuplicateTargetFound": "Yinelenen hedef bulundu", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Sağlıklı", + "healthCheckUnhealthy": "Sağlıksız", + "healthCheckUnknown": "Bilinmiyor", + "healthCheck": "Sağlık Kontrolü", + "configureHealthCheck": "Sağlık Kontrolünü Yapılandır", + "configureHealthCheckDescription": "{hedef} için sağlık izleme kurun", + "enableHealthChecks": "Sağlık Kontrollerini Etkinleştir", + "enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.", + "healthScheme": "Yöntem", + "healthSelectScheme": "Yöntem Seç", + "healthCheckPath": "Yol", + "healthHostname": "IP / Hostname", + "healthPort": "Bağlantı Noktası", + "healthCheckPathDescription": "Sağlık durumunu kontrol etmek için yol.", + "healthyIntervalSeconds": "Sağlıklı Aralık", + "unhealthyIntervalSeconds": "Sağlıksız Aralık", + "IntervalSeconds": "Sağlıklı Aralık", + "timeoutSeconds": "Zaman Aşımı", + "timeIsInSeconds": "Zaman saniye cinsindendir", + "retryAttempts": "Tekrar Deneme Girişimleri", + "expectedResponseCodes": "Beklenen Yanıt Kodları", + "expectedResponseCodesDescription": "Sağlıklı durumu gösteren HTTP durum kodu. Boş bırakılırsa, 200-300 arası sağlıklı kabul edilir.", "customHeaders": "Özel Başlıklar", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Başlıklar yeni satırla ayrılmış: Başlık-Adı: değer", + "headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.", + "saveHealthCheck": "Sağlık Kontrolünü Kaydet", + "healthCheckSaved": "Sağlık Kontrolü Kaydedildi", + "healthCheckSavedDescription": "Sağlık kontrol yapılandırması başarıyla kaydedildi", + "healthCheckError": "Sağlık Kontrol Hatası", + "healthCheckErrorDescription": "Sağlık kontrol yapılandırması kaydedilirken bir hata oluştu", + "healthCheckPathRequired": "Sağlık kontrol yolu gereklidir", + "healthCheckMethodRequired": "HTTP yöntemi gereklidir", + "healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır", + "healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır", + "healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır", "httpMethod": "HTTP Yöntemi", "selectHttpMethod": "HTTP yöntemini seçin", "domainPickerSubdomainLabel": "Alt Alan Adı", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Mevcut ücretsiz alan adları arasından aramak ve seçmek için bir alt alan adı girin.", "domainPickerFreeDomains": "Ücretsiz Alan Adları", "domainPickerSearchForAvailableDomains": "Mevcut alan adlarını ara", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Not: Ücretsiz sağlanan alan adları şu anda öz-host edilmiş örnekler için kullanılabilir değildir.", "resourceDomain": "Alan Adı", "resourceEditDomain": "Alan Adını Düzenle", "siteName": "Site Adı", @@ -1543,72 +1543,72 @@ "autoLoginError": "Otomatik Giriş Hatası", "autoLoginErrorNoRedirectUrl": "Kimlik sağlayıcıdan yönlendirme URL'si alınamadı.", "autoLoginErrorGeneratingUrl": "Kimlik doğrulama URL'si oluşturulamadı.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Öz-Host Yönetim", + "remoteExitNodeDescription": "Ağ bağlantınızı genişletmek için düğümleri yönetin", + "remoteExitNodes": "Düğümler", + "searchRemoteExitNodes": "Düğüm ara...", + "remoteExitNodeAdd": "Düğüm Ekle", + "remoteExitNodeErrorDelete": "Düğüm silinirken hata oluştu", + "remoteExitNodeQuestionRemove": "{selectedNode} düğümünü organizasyondan kaldırmak istediğinizden emin misiniz?", + "remoteExitNodeMessageRemove": "Kaldırıldığında, düğüm artık erişilebilir olmayacaktır.", + "remoteExitNodeMessageConfirm": "Onaylamak için lütfen aşağıya düğümün adını yazın.", + "remoteExitNodeConfirmDelete": "Düğüm Silmeyi Onayla", + "remoteExitNodeDelete": "Düğümü Sil", + "sidebarRemoteExitNodes": "Düğümler", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Düğüm Oluştur", + "description": "Ağ bağlantınızı genişletmek için yeni bir düğüm oluşturun", + "viewAllButton": "Tüm Düğümleri Gör", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Oluşturma Stratejisi", + "description": "Düğümünüzü manuel olarak yapılandırmak veya yeni kimlik bilgileri oluşturmak için bunu seçin.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Düğüm Benimse", + "description": "Zaten düğüm için kimlik bilgilerine sahipseniz bunu seçin." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Anahtarları Oluştur", + "description": "Düğüm için yeni anahtarlar oluşturmak istiyorsanız bunu seçin" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Mevcut Düğümü Benimse", + "description": "Adayacağınız mevcut düğümün kimlik bilgilerini girin", + "nodeIdLabel": "Düğüm ID", + "nodeIdDescription": "Adayacağınız mevcut düğümün ID'si", + "secretLabel": "Gizli", + "secretDescription": "Mevcut düğümün gizli anahtarı", + "submitButton": "Düğümü Benimse" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Oluşturulan Kimlik Bilgileri", + "description": "Düğümünüzü yapılandırmak için oluşturulan bu kimlik bilgilerini kullanın", + "nodeIdTitle": "Düğüm ID", + "secretTitle": "Gizli", + "saveCredentialsTitle": "Kimlik Bilgilerini Yapılandırmaya Ekle", + "saveCredentialsDescription": "Bağlantıyı tamamlamak için bu kimlik bilgilerini öz-host Pangolin düğüm yapılandırma dosyanıza ekleyin.", + "submitButton": "Düğüm Oluştur" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "Mevcut bir düğümü benimserken Düğüm ID ve Gizli anahtar gereklidir" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Varsayılanlar yüklenemedi", + "defaultsNotLoaded": "Varsayılanlar yüklenmedi", + "createFailed": "Düğüm oluşturulamadı" }, "success": { - "created": "Node created successfully" + "created": "Düğüm başarıyla oluşturuldu" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Düğüm Seçimi", + "remoteExitNodeSelectionDescription": "Yerel site için trafiği yönlendirecek düğümü seçin", + "remoteExitNodeRequired": "Yerel siteler için bir düğüm seçilmelidir", + "noRemoteExitNodesAvailable": "Düğüm Bulunamadı", + "noRemoteExitNodesAvailableDescription": "Bu organizasyon için düğüm mevcut değil. Yerel siteleri kullanmak için önce bir düğüm oluşturun.", + "exitNode": "Çıkış Düğümü", + "country": "Ülke", + "rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak", "managedSelfHosted": { "title": "Yönetilen Self-Hosted", "description": "Daha güvenilir ve düşük bakım gerektiren, ekstra özelliklere sahip kendi kendine barındırabileceğiniz Pangolin sunucusu", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Uluslararası Alan Adı Tespit Edildi", "willbestoredas": "Şu şekilde depolanacak:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Otomatik Sağlama etkinleştirildiğinde kullanıcıların oturum açarken rollerin nasıl atandığını belirleyin.", + "selectRole": "Bir Rol Seçin", + "roleMappingExpression": "İfade", + "selectRolePlaceholder": "Bir rol seçin", + "selectRoleDescription": "Bu kimlik sağlayıcısından tüm kullanıcılara atanacak bir rol seçin", + "roleMappingExpressionDescription": "Rol bilgilerini ID tokeninden çıkarmak için bir JMESPath ifadesi girin", + "idpTenantIdRequired": "Kiracı Kimliği gereklidir", + "invalidValue": "Geçersiz değer", + "idpTypeLabel": "Kimlik Sağlayıcı Türü", + "roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'", + "idpGoogleConfiguration": "Google Yapılandırması", + "idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın", + "idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz", + "idpGoogleClientSecretDescription": "Google OAuth2 İstemci Sırrınız", + "idpAzureConfiguration": "Azure Entra ID Yapılandırması", + "idpAzureConfigurationDescription": "Azure Entra ID OAuth2 kimlik bilgilerinizi yapılandırın", + "idpTenantId": "Kiracı Kimliği", + "idpTenantIdPlaceholder": "kiraci-kimliginiz", + "idpAzureTenantIdDescription": "Azure kiracı kimliğiniz (Azure Active Directory genel bakışında bulunur)", + "idpAzureClientIdDescription": "Azure Uygulama Kaydı İstemci Kimliğiniz", + "idpAzureClientSecretDescription": "Azure Uygulama Kaydı İstemci Sırrınız", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google Yapılandırması", + "idpAzureConfigurationTitle": "Azure Entra ID Yapılandırması", + "idpTenantIdLabel": "Kiracı Kimliği", + "idpAzureClientIdDescription2": "Azure Uygulama Kaydı İstemci Kimliğiniz", + "idpAzureClientSecretDescription2": "Azure Uygulama Kaydı İstemci Sırrınız", "idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Alt ağ", + "subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.", + "authPage": "Yetkilendirme Sayfası", + "authPageDescription": "Kuruluşunuz için yetkilendirme sayfasını yapılandırın", + "authPageDomain": "Yetkilendirme Sayfası Alanı", + "noDomainSet": "Alan belirlenmedi", + "changeDomain": "Alanı Değiştir", + "selectDomain": "Alan Seçin", + "restartCertificate": "Sertifikayı Yenile", + "editAuthPageDomain": "Yetkilendirme Sayfası Alanını Düzenle", + "setAuthPageDomain": "Yetkilendirme Sayfası Alanını Ayarla", + "failedToFetchCertificate": "Sertifika getirilemedi", + "failedToRestartCertificate": "Sertifika yeniden başlatılamadı", + "addDomainToEnableCustomAuthPages": "Kuruluşunuz için özel kimlik doğrulama sayfalarını etkinleştirmek için bir alan ekleyin", + "selectDomainForOrgAuthPage": "Kuruluşun kimlik doğrulama sayfası için bir alan seçin", "domainPickerProvidedDomain": "Sağlanan Alan Adı", "domainPickerFreeProvidedDomain": "Ücretsiz Sağlanan Alan Adı", "domainPickerVerified": "Doğrulandı", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" {domain} için geçerli yapılamadı.", "domainPickerSubdomainSanitized": "Alt alan adı temizlendi", "domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Kuruluşunuza giriş yapın", + "orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin", + "orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.", + "orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap", + "subscriptionRequiredToUse": "Bu özelliği kullanmak için abonelik gerekmektedir.", + "idpDisabled": "Kimlik sağlayıcılar devre dışı bırakılmıştır.", + "orgAuthPageDisabled": "Kuruluş kimlik doğrulama sayfası devre dışı bırakılmıştır.", + "domainRestartedDescription": "Alan doğrulaması başarıyla yeniden başlatıldı", "resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml", "emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", "twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Kimlik doğrulama sayfası ayarları güncellenirken bir hata oluştu.", + "authPageUpdated": "Kimlik doğrulama sayfası başarıyla güncellendi", + "healthCheckNotAvailable": "Yerel", + "rewritePath": "Yolu Yeniden Yaz", + "rewritePathDescription": "Seçenek olarak hedefe iletmeden önce yolu yeniden yazın." } From 346d886f8a4111658b6a9a315acfbf842901dce0 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:06 -0700 Subject: [PATCH 153/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 380 ++++++++++++++++++++++---------------------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 8314f092..94ccd9ae 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。", "siteWg": "基本 WireGuard", "siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "使用任何WireGuard客户端建立隧道。需要手动配置NAT。仅适用于自托管节点。", "siteLocalDescription": "仅限本地资源。不需要隧道。", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "仅本地资源。无需隧道。仅适用于自托管节点。", "siteSeeAll": "查看所有站点", "siteTunnelDescription": "确定如何连接到您的网站", "siteNewtCredentials": "Newt 凭据", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS 资源", "resourceHTTPDescription": "使用子域或根域名通过 HTTPS 向您的应用程序提出代理请求。", "resourceRaw": "TCP/UDP 资源", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "使用 TCP/UDP 使用端口号向您的应用提出代理请求。", "resourceCreate": "创建资源", "resourceCreateDescription": "按照下面的步骤创建新资源", "resourceSeeAll": "查看所有资源", @@ -168,9 +168,9 @@ "siteSelect": "选择站点", "siteSearch": "搜索站点", "siteNotFound": "未找到站点。", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "选择国家", + "searchCountries": "搜索国家...", + "noCountryFound": "找不到国家。", "siteSelectionDescription": "此站点将为目标提供连接。", "resourceType": "资源类型", "resourceTypeDescription": "确定如何访问您的资源", @@ -598,7 +598,7 @@ "newtErrorFetchReleases": "无法获取版本信息: {err}", "newtErrorFetchLatest": "无法获取最新版信息: {err}", "newtEndpoint": "Newt 端点", - "newtId": "Newt ID", + "newtId": "Newt ID", "newtSecretKey": "Newt 私钥", "architecture": "架构", "sites": "站点", @@ -1156,7 +1156,7 @@ "containerLabels": "标签", "containerLabelsCount": "{count, plural, other {# 标签}}", "containerLabelsTitle": "容器标签", - "containerLabelEmpty": "", + "containerLabelEmpty": "<为空>", "containerPorts": "端口", "containerPortsMore": "+{count} 更多", "containerActions": "行动", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "子域:{subdomain}", "domainPickerNamespace": "命名空间:{namespace}", "domainPickerShowMore": "显示更多", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "选择区域", + "regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。", + "regionSelectorPlaceholder": "选择一个区域", + "regionSelectorComingSoon": "即将推出", + "billingLoadingSubscription": "正在加载订阅...", + "billingFreeTier": "免费层", + "billingWarningOverLimit": "警告:您已超出一个或多个使用限制。在您修改订阅或调整使用情况之前,您的站点将无法连接。", + "billingUsageLimitsOverview": "使用限制概览", + "billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@fossorial.io。", + "billingDataUsage": "数据使用情况", + "billingOnlineTime": "站点在线时间", + "billingUsers": "活跃用户", + "billingDomains": "活跃域", + "billingRemoteExitNodes": "活跃自托管节点", + "billingNoLimitConfigured": "未配置限制", + "billingEstimatedPeriod": "估计结算周期", + "billingIncludedUsage": "包含的使用量", + "billingIncludedUsageDescription": "您当前订阅计划中包含的使用量", + "billingFreeTierIncludedUsage": "免费层使用额度", + "billingIncluded": "包含", + "billingEstimatedTotal": "预计总额:", + "billingNotes": "备注", + "billingEstimateNote": "这是根据您当前使用情况的估算。", + "billingActualChargesMayVary": "实际费用可能会有变化。", + "billingBilledAtEnd": "您将在结算周期结束时被计费。", + "billingModifySubscription": "修改订阅", + "billingStartSubscription": "开始订阅", + "billingRecurringCharge": "周期性收费", + "billingManageSubscriptionSettings": "管理您的订阅设置和偏好", + "billingNoActiveSubscription": "您没有活跃的订阅。开始订阅以增加使用限制。", + "billingFailedToLoadSubscription": "无法加载订阅", + "billingFailedToLoadUsage": "无法加载使用情况", + "billingFailedToGetCheckoutUrl": "无法获取结账网址", + "billingPleaseTryAgainLater": "请稍后再试。", + "billingCheckoutError": "结账错误", + "billingFailedToGetPortalUrl": "无法获取门户网址", + "billingPortalError": "门户错误", + "billingDataUsageInfo": "当连接到云端时,您将为通过安全隧道传输的所有数据收取费用。 这包括您所有站点的进出流量。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取数据。", + "billingOnlineTimeInfo": "您要根据您的网站连接到云端的时间长短收取费用。 例如,44,640分钟等于一个24/7全月运行的网站。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取费用。", + "billingUsersInfo": "根据您组织中的活跃用户数量收费。按日计算账单。", + "billingDomainInfo": "根据组织中活跃域的数量收费。按日计算账单。", + "billingRemoteExitNodesInfo": "根据您组织中已管理节点的数量收费。按日计算账单。", "domainNotFound": "域未找到", "domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。", "failed": "失败", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS 更改可能需要一些时间才能在互联网上传播。这可能需要从几分钟到 48 小时,具体取决于您的 DNS 提供商和 TTL 设置。", "resourcePortRequired": "非 HTTP 资源必须输入端口号", "resourcePortNotAllowed": "HTTP 资源不应设置端口号", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "价格计算器", "signUpTerms": { "IAgreeToThe": "我同意", "termsOfService": "服务条款", @@ -1390,7 +1390,7 @@ "clientOlmCredentials": "Olm 凭据", "clientOlmCredentialsDescription": "这是 Olm 服务器的身份验证方式", "olmEndpoint": "Olm 端点", - "olmId": "Olm ID", + "olmId": "Olm ID", "olmSecretKey": "Olm 私钥", "clientCredentialsSave": "保存您的凭据", "clientCredentialsSaveDescription": "该信息仅会显示一次,请确保将其复制到安全位置。", @@ -1412,41 +1412,41 @@ "addNewTarget": "添加新目标", "targetsList": "目标列表", "targetErrorDuplicateTargetFound": "找到重复的目标", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "正常", + "healthCheckUnhealthy": "不正常", + "healthCheckUnknown": "未知", + "healthCheck": "健康检查", + "configureHealthCheck": "配置健康检查", + "configureHealthCheckDescription": "为 {target} 设置健康监控", + "enableHealthChecks": "启用健康检查", + "enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。", + "healthScheme": "方法", + "healthSelectScheme": "选择方法", + "healthCheckPath": "路径", + "healthHostname": "IP / 主机", + "healthPort": "端口", + "healthCheckPathDescription": "用于检查健康状态的路径。", + "healthyIntervalSeconds": "正常间隔", + "unhealthyIntervalSeconds": "不正常间隔", + "IntervalSeconds": "正常间隔", + "timeoutSeconds": "超时", + "timeIsInSeconds": "时间以秒为单位", + "retryAttempts": "重试次数", + "expectedResponseCodes": "期望响应代码", + "expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空,200-300 被视为健康。", "customHeaders": "自定义标题", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "头部新行分隔:头部名称:值", + "headersValidationError": "头部必须是格式:头部名称:值。", + "saveHealthCheck": "保存健康检查", + "healthCheckSaved": "健康检查已保存", + "healthCheckSavedDescription": "健康检查配置已成功保存。", + "healthCheckError": "健康检查错误", + "healthCheckErrorDescription": "保存健康检查配置时出错", + "healthCheckPathRequired": "健康检查路径为必填项", + "healthCheckMethodRequired": "HTTP 方法为必填项", + "healthCheckIntervalMin": "检查间隔必须至少为 5 秒", + "healthCheckTimeoutMin": "超时必须至少为 1 秒", + "healthCheckRetryMin": "重试次数必须至少为 1 次", "httpMethod": "HTTP 方法", "selectHttpMethod": "选择 HTTP 方法", "domainPickerSubdomainLabel": "子域名", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "输入一个子域名以搜索并从可用免费域名中选择。", "domainPickerFreeDomains": "免费域名", "domainPickerSearchForAvailableDomains": "搜索可用域名", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "注意:自托管实例当前不提供免费的域名。", "resourceDomain": "域名", "resourceEditDomain": "编辑域名", "siteName": "站点名称", @@ -1543,72 +1543,72 @@ "autoLoginError": "自动登录错误", "autoLoginErrorNoRedirectUrl": "未从身份提供商收到重定向URL。", "autoLoginErrorGeneratingUrl": "生成身份验证URL失败。", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "管理自托管", + "remoteExitNodeDescription": "管理节点以扩展您的网络连接", + "remoteExitNodes": "节点", + "searchRemoteExitNodes": "搜索节点...", + "remoteExitNodeAdd": "添加节点", + "remoteExitNodeErrorDelete": "删除节点时出错", + "remoteExitNodeQuestionRemove": "您确定要从组织中删除 {selectedNode} 节点吗?", + "remoteExitNodeMessageRemove": "一旦删除,该节点将不再能够访问。", + "remoteExitNodeMessageConfirm": "要确认,请输入以下节点的名称。", + "remoteExitNodeConfirmDelete": "确认删除节点", + "remoteExitNodeDelete": "删除节点", + "sidebarRemoteExitNodes": "节点", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "创建节点", + "description": "创建一个新节点来扩展您的网络连接", + "viewAllButton": "查看所有节点", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "创建策略", + "description": "选择此选项以手动配置您的节点或生成新凭据。", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "采纳节点", + "description": "如果您已经拥有该节点的凭据,请选择此项。" }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "生成密钥", + "description": "如果您想为节点生成新密钥,请选择此选项" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "采纳现有节点", + "description": "输入您想要采用的现有节点的凭据", + "nodeIdLabel": "节点 ID", + "nodeIdDescription": "您想要采用的现有节点的 ID", + "secretLabel": "密钥", + "secretDescription": "现有节点的秘密密钥", + "submitButton": "采用节点" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "生成的凭据", + "description": "使用这些生成的凭据来配置您的节点", + "nodeIdTitle": "节点 ID", + "secretTitle": "密钥", + "saveCredentialsTitle": "将凭据添加到配置中", + "saveCredentialsDescription": "将这些凭据添加到您的自托管 Pangolin 节点配置文件中以完成连接。", + "submitButton": "创建节点" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "在通过现有节点时需要节点ID和密钥" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "无法加载默认值", + "defaultsNotLoaded": "默认值未加载", + "createFailed": "创建节点失败" }, "success": { - "created": "Node created successfully" + "created": "节点创建成功" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "节点选择", + "remoteExitNodeSelectionDescription": "为此本地站点选择要路由流量的节点", + "remoteExitNodeRequired": "必须为本地站点选择节点", + "noRemoteExitNodesAvailable": "无可用节点", + "noRemoteExitNodesAvailableDescription": "此组织没有可用的节点。首先创建一个节点来使用本地站点。", + "exitNode": "出口节点", + "country": "国家", + "rulesMatchCountry": "当前基于源 IP", "managedSelfHosted": { "title": "托管自托管", "description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "检测到国际域", "willbestoredas": "储存为:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", - "idpGoogleTitle": "Google", + "roleMappingDescription": "确定当用户启用自动配送时如何分配他们的角色。", + "selectRole": "选择角色", + "roleMappingExpression": "表达式", + "selectRolePlaceholder": "选择角色", + "selectRoleDescription": "选择一个角色,从此身份提供商分配给所有用户", + "roleMappingExpressionDescription": "输入一个 JMESPath 表达式来从 ID 令牌提取角色信息", + "idpTenantIdRequired": "租户ID是必需的", + "invalidValue": "无效的值", + "idpTypeLabel": "身份提供者类型", + "roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'", + "idpGoogleConfiguration": "Google 配置", + "idpGoogleConfigurationDescription": "配置您的 Google OAuth2 凭据", + "idpGoogleClientIdDescription": "您的 Google OAuth2 客户端 ID", + "idpGoogleClientSecretDescription": "您的 Google OAuth2 客户端密钥", + "idpAzureConfiguration": "Azure Entra ID 配置", + "idpAzureConfigurationDescription": "配置您的 Azure Entra ID OAuth2 凭据", + "idpTenantId": "租户 ID", + "idpTenantIdPlaceholder": "您的租户ID", + "idpAzureTenantIdDescription": "您的 Azure 租户ID (在 Azure Active Directory 概览中发现)", + "idpAzureClientIdDescription": "您的 Azure 应用程序注册客户端 ID", + "idpAzureClientSecretDescription": "您的 Azure 应用程序注册客户端密钥", + "idpGoogleTitle": "谷歌", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google 配置", + "idpAzureConfigurationTitle": "Azure Entra ID 配置", + "idpTenantIdLabel": "租户 ID", + "idpAzureClientIdDescription2": "您的 Azure 应用程序注册客户端 ID", + "idpAzureClientSecretDescription2": "您的 Azure 应用程序注册客户端密钥", "idpGoogleDescription": "Google OAuth2/OIDC 提供商", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "子网", + "subnetDescription": "此组织网络配置的子网。", + "authPage": "认证页面", + "authPageDescription": "配置您的组织认证页面", + "authPageDomain": "认证页面域", + "noDomainSet": "没有域设置", + "changeDomain": "更改域", + "selectDomain": "选择域", + "restartCertificate": "重新启动证书", + "editAuthPageDomain": "编辑认证页面域", + "setAuthPageDomain": "设置认证页面域", + "failedToFetchCertificate": "获取证书失败", + "failedToRestartCertificate": "重新启动证书失败", + "addDomainToEnableCustomAuthPages": "为您的组织添加域名以启用自定义认证页面", + "selectDomainForOrgAuthPage": "选择组织认证页面的域", "domainPickerProvidedDomain": "提供的域", "domainPickerFreeProvidedDomain": "免费提供的域", "domainPickerVerified": "已验证", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" 无法为 {domain} 变为有效。", "domainPickerSubdomainSanitized": "子域已净化", "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "登录到您的组织", + "orgAuthChooseIdpDescription": "选择您的身份提供商以继续", + "orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。", + "orgAuthSignInWithPangolin": "使用 Pangolin 登录", + "subscriptionRequiredToUse": "需要订阅才能使用此功能。", + "idpDisabled": "身份提供者已禁用。", + "orgAuthPageDisabled": "组织认证页面已禁用。", + "domainRestartedDescription": "域验证重新启动成功", "resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "编辑文件:docker-compose.yml", "emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", "twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "更新身份验证页面设置时出错", + "authPageUpdated": "身份验证页面更新成功", + "healthCheckNotAvailable": "本地的", + "rewritePath": "重写路径", + "rewritePathDescription": "在转发到目标之前,可以选择重写路径。" } From 0624087373bce7a6926c8ed4dbee22bb540eafae Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:08 -0700 Subject: [PATCH 154/322] New translations en-us.json (Norwegian Bokmal) --- messages/nb-NO.json | 372 ++++++++++++++++++++++---------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/messages/nb-NO.json b/messages/nb-NO.json index 8d789949..84dc5266 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "Enkleste måte å opprette et inngangspunkt i nettverket ditt. Ingen ekstra oppsett.", "siteWg": "Grunnleggende WireGuard", "siteWgDescription": "Bruk hvilken som helst WireGuard-klient for å etablere en tunnel. Manuell NAT-oppsett kreves.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Bruk hvilken som helst WireGuard-klient for å etablere en tunnel. Manuell NAT-oppsett er nødvendig. FUNGERER KUN PÅ SELVHOSTEDE NODER", "siteLocalDescription": "Kun lokale ressurser. Ingen tunnelering.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Kun lokale ressurser. Ingen tunneling. FUNGERER KUN PÅ SELVHOSTEDE NODER", "siteSeeAll": "Se alle områder", "siteTunnelDescription": "Bestem hvordan du vil koble deg til ditt område", "siteNewtCredentials": "Newt påloggingsinformasjon", @@ -118,7 +118,7 @@ "usageExamples": "Brukseksempler", "tokenId": "Token-ID", "requestHeades": "Request Headers", - "queryParameter": "Query Parameter", + "queryParameter": "Forespørsel Params", "importantNote": "Viktig merknad", "shareImportantDescription": "Av sikkerhetsgrunner anbefales det å bruke headere fremfor query parametere der det er mulig, da query parametere kan logges i serverlogger eller nettleserhistorikk.", "token": "Token", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS-ressurs", "resourceHTTPDescription": "Proxy-forespørsler til appen din over HTTPS ved bruk av et underdomene eller grunndomene.", "resourceRaw": "Rå TCP/UDP-ressurs", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Proxyer forespørsler til appen din over TCP/UDP ved å bruke et portnummer.", "resourceCreate": "Opprett ressurs", "resourceCreateDescription": "Følg trinnene nedenfor for å opprette en ny ressurs", "resourceSeeAll": "Se alle ressurser", @@ -168,9 +168,9 @@ "siteSelect": "Velg område", "siteSearch": "Søk i område", "siteNotFound": "Ingen område funnet.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Velg land", + "searchCountries": "Søk land...", + "noCountryFound": "Ingen land funnet.", "siteSelectionDescription": "Dette området vil gi tilkobling til mål.", "resourceType": "Ressurstype", "resourceTypeDescription": "Bestem hvordan du vil få tilgang til ressursen din", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Underdomene: {subdomain}", "domainPickerNamespace": "Navnerom: {namespace}", "domainPickerShowMore": "Vis mer", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Velg Region", + "regionSelectorInfo": "Å velge en region hjelper oss med å gi bedre ytelse for din lokasjon. Du trenger ikke være i samme region som serveren.", + "regionSelectorPlaceholder": "Velg en region", + "regionSelectorComingSoon": "Kommer snart", + "billingLoadingSubscription": "Laster abonnement...", + "billingFreeTier": "Gratis nivå", + "billingWarningOverLimit": "Advarsel: Du har overskredet en eller flere bruksgrenser. Nettstedene dine vil ikke koble til før du endrer abonnementet ditt eller justerer bruken.", + "billingUsageLimitsOverview": "Oversikt over bruksgrenser", + "billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@fossorial.io.", + "billingDataUsage": "Databruk", + "billingOnlineTime": "Online tid for nettsteder", + "billingUsers": "Aktive brukere", + "billingDomains": "Aktive domener", + "billingRemoteExitNodes": "Aktive selvstyrte noder", + "billingNoLimitConfigured": "Ingen grense konfigurert", + "billingEstimatedPeriod": "Estimert faktureringsperiode", + "billingIncludedUsage": "Inkludert Bruk", + "billingIncludedUsageDescription": "Bruk inkludert i din nåværende abonnementsplan", + "billingFreeTierIncludedUsage": "Gratis nivå bruksgrenser", + "billingIncluded": "inkludert", + "billingEstimatedTotal": "Estimert Totalt:", + "billingNotes": "Notater", + "billingEstimateNote": "Dette er et estimat basert på din nåværende bruk.", + "billingActualChargesMayVary": "Faktiske kostnader kan variere.", + "billingBilledAtEnd": "Du vil bli fakturert ved slutten av faktureringsperioden.", + "billingModifySubscription": "Endre abonnement", + "billingStartSubscription": "Start abonnement", + "billingRecurringCharge": "Innkommende Avgift", + "billingManageSubscriptionSettings": "Administrer abonnementsinnstillinger og preferanser", + "billingNoActiveSubscription": "Du har ikke et aktivt abonnement. Start abonnementet ditt for å øke bruksgrensene.", + "billingFailedToLoadSubscription": "Klarte ikke å laste abonnement", + "billingFailedToLoadUsage": "Klarte ikke å laste bruksdata", + "billingFailedToGetCheckoutUrl": "Mislyktes å få betalingslenke", + "billingPleaseTryAgainLater": "Vennligst prøv igjen senere.", + "billingCheckoutError": "Kasserror", + "billingFailedToGetPortalUrl": "Mislyktes å hente portal URL", + "billingPortalError": "Portalfeil", + "billingDataUsageInfo": "Du er ladet for all data som overføres gjennom dine sikre tunneler når du er koblet til skyen. Dette inkluderer både innkommende og utgående trafikk på alle dine nettsteder. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Data belastes ikke ved bruk av EK-grupper.", + "billingOnlineTimeInfo": "Du er ladet på hvor lenge sidene dine forblir koblet til skyen. For eksempel tilsvarer 44,640 minutter ett nettsted som går 24/7 i en hel måned. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Tid belastes ikke når du bruker noder.", + "billingUsersInfo": "Du belastes for hver bruker i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive brukerkontoer i organisasjonen din.", + "billingDomainInfo": "Du belastes for hvert domene i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive domenekontoer i organisasjonen din.", + "billingRemoteExitNodesInfo": "Du belastes for hver styrt node i organisasjonen din. Faktureringen beregnes daglig basert på antall aktive styrte noder i organisasjonen din.", "domainNotFound": "Domene ikke funnet", "domainNotFoundDescription": "Denne ressursen er deaktivert fordi domenet ikke lenger eksisterer i systemet vårt. Vennligst angi et nytt domene for denne ressursen.", "failed": "Mislyktes", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "DNS-endringer kan ta litt tid å propagere over internett. Dette kan ta fra noen få minutter til 48 timer, avhengig av din DNS-leverandør og TTL-innstillinger.", "resourcePortRequired": "Portnummer er påkrevd for ikke-HTTP-ressurser", "resourcePortNotAllowed": "Portnummer skal ikke angis for HTTP-ressurser", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Pris Kalkulator", "signUpTerms": { "IAgreeToThe": "Jeg godtar", "termsOfService": "brukervilkårene", @@ -1412,41 +1412,41 @@ "addNewTarget": "Legg til nytt mål", "targetsList": "Liste over mål", "targetErrorDuplicateTargetFound": "Duplikat av mål funnet", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", + "healthCheckHealthy": "Sunn", + "healthCheckUnhealthy": "Usunn", + "healthCheckUnknown": "Ukjent", + "healthCheck": "Helsekontroll", + "configureHealthCheck": "Konfigurer Helsekontroll", + "configureHealthCheckDescription": "Sett opp helsekontroll for {target}", + "enableHealthChecks": "Aktiver Helsekontroller", + "enableHealthChecksDescription": "Overvåk helsen til dette målet. Du kan overvåke et annet endepunkt enn målet hvis nødvendig.", + "healthScheme": "Metode", + "healthSelectScheme": "Velg metode", + "healthCheckPath": "Sti", + "healthHostname": "IP / Vert", "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckPathDescription": "Stien for å sjekke helsestatus.", + "healthyIntervalSeconds": "Sunt intervall", + "unhealthyIntervalSeconds": "Usunt intervall", + "IntervalSeconds": "Sunt intervall", + "timeoutSeconds": "Tidsavbrudd", + "timeIsInSeconds": "Tid er i sekunder", + "retryAttempts": "Forsøk på nytt", + "expectedResponseCodes": "Forventede svarkoder", + "expectedResponseCodesDescription": "HTTP-statuskode som indikerer sunn status. Hvis den blir stående tom, regnes 200-300 som sunn.", "customHeaders": "Egendefinerte topptekster", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Overskrifter som er adskilt med linje: Overskriftsnavn: verdi", + "headersValidationError": "Topptekst må være i formatet: header-navn: verdi.", + "saveHealthCheck": "Lagre Helsekontroll", + "healthCheckSaved": "Helsekontroll Lagret", + "healthCheckSavedDescription": "Helsekontrollkonfigurasjonen ble lagret", + "healthCheckError": "Helsekontrollfeil", + "healthCheckErrorDescription": "Det oppstod en feil under lagring av helsekontrollkonfigurasjonen", + "healthCheckPathRequired": "Helsekontrollsti er påkrevd", + "healthCheckMethodRequired": "HTTP-metode er påkrevd", + "healthCheckIntervalMin": "Sjekkeintervallet må være minst 5 sekunder", + "healthCheckTimeoutMin": "Timeout må være minst 1 sekund", + "healthCheckRetryMin": "Forsøk på nytt må være minst 1", "httpMethod": "HTTP-metode", "selectHttpMethod": "Velg HTTP-metode", "domainPickerSubdomainLabel": "Underdomene", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Skriv inn et underdomene for å søke og velge blant tilgjengelige gratis domener.", "domainPickerFreeDomains": "Gratis domener", "domainPickerSearchForAvailableDomains": "Søk etter tilgjengelige domener", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Merk: Gratis tilbudte domener er ikke tilgjengelig for selv-hostede instanser akkurat nå.", "resourceDomain": "Domene", "resourceEditDomain": "Rediger domene", "siteName": "Områdenavn", @@ -1543,72 +1543,72 @@ "autoLoginError": "Feil ved automatisk innlogging", "autoLoginErrorNoRedirectUrl": "Ingen omdirigerings-URL mottatt fra identitetsleverandøren.", "autoLoginErrorGeneratingUrl": "Kunne ikke generere autentiserings-URL.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Administrer Selv-Hostet", + "remoteExitNodeDescription": "Administrer noder for å forlenge nettverkstilkoblingen din", + "remoteExitNodes": "Noder", + "searchRemoteExitNodes": "Søk noder...", + "remoteExitNodeAdd": "Legg til Node", + "remoteExitNodeErrorDelete": "Feil ved sletting av node", + "remoteExitNodeQuestionRemove": "Er du sikker på at du vil fjerne noden {selectedNode} fra organisasjonen?", + "remoteExitNodeMessageRemove": "Når noden er fjernet, vil ikke lenger være tilgjengelig.", + "remoteExitNodeMessageConfirm": "For å bekrefte, skriv inn navnet på noden nedenfor.", + "remoteExitNodeConfirmDelete": "Bekreft sletting av Node", + "remoteExitNodeDelete": "Slett Node", + "sidebarRemoteExitNodes": "Noder", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Opprett node", + "description": "Opprett en ny node for å utvide nettverkstilkoblingen din", + "viewAllButton": "Vis alle koder", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Opprettelsesstrategi", + "description": "Velg denne for manuelt å konfigurere noden eller generere nye legitimasjoner.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adopter Node", + "description": "Velg dette hvis du allerede har legitimasjon til noden." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Generer Nøkler", + "description": "Velg denne hvis du vil generere nye nøkler for noden" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Adopter Eksisterende Node", + "description": "Skriv inn opplysningene til den eksisterende noden du vil adoptere", + "nodeIdLabel": "Node-ID", + "nodeIdDescription": "ID-en til den eksisterende noden du vil adoptere", + "secretLabel": "Sikkerhetsnøkkel", + "secretDescription": "Den hemmelige nøkkelen til en eksisterende node", + "submitButton": "Adopter Node" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Genererte Legitimasjoner", + "description": "Bruk disse genererte opplysningene for å konfigurere noden din", + "nodeIdTitle": "Node-ID", + "secretTitle": "Sikkerhet", + "saveCredentialsTitle": "Legg til Legitimasjoner til Config", + "saveCredentialsDescription": "Legg til disse legitimasjonene i din selv-hostede Pangolin node-konfigurasjonsfil for å fullføre koblingen.", + "submitButton": "Opprett node" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "Node ID og Secret er påkrevd når du adopterer en eksisterende node" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Feil ved lasting av standarder", + "defaultsNotLoaded": "Standarder ikke lastet", + "createFailed": "Kan ikke opprette node" }, "success": { - "created": "Node created successfully" + "created": "Node opprettet" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Noden utvalg", + "remoteExitNodeSelectionDescription": "Velg en node for å sende trafikk gjennom for dette lokale nettstedet", + "remoteExitNodeRequired": "En node må velges for lokale nettsteder", + "noRemoteExitNodesAvailable": "Ingen noder tilgjengelig", + "noRemoteExitNodesAvailableDescription": "Ingen noder er tilgjengelige for denne organisasjonen. Opprett en node først for å bruke lokale nettsteder.", + "exitNode": "Utgangsnode", + "country": "Land", + "rulesMatchCountry": "For tiden basert på kilde IP", "managedSelfHosted": { "title": "Administrert selv-hostet", "description": "Sikre og lavvedlikeholdsservere, selvbetjente Pangolin med ekstra klokker, og understell", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Internasjonalt domene oppdaget", "willbestoredas": "Vil bli lagret som:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Bestem hvordan roller tilordnes brukere når innloggingen er aktivert når autog-rapportering er aktivert.", + "selectRole": "Velg en rolle", + "roleMappingExpression": "Uttrykk", + "selectRolePlaceholder": "Velg en rolle", + "selectRoleDescription": "Velg en rolle å tilordne alle brukere fra denne identitet leverandøren", + "roleMappingExpressionDescription": "Skriv inn et JMESPath uttrykk for å hente rolleinformasjon fra ID-nøkkelen", + "idpTenantIdRequired": "Bedriftens ID kreves", + "invalidValue": "Ugyldig verdi", + "idpTypeLabel": "Identitet leverandør type", + "roleMappingExpressionPlaceholder": "F.eks. inneholder(grupper, 'admin') && 'Admin' ⋅'Medlem'", + "idpGoogleConfiguration": "Google Konfigurasjon", + "idpGoogleConfigurationDescription": "Konfigurer din Google OAuth2 legitimasjon", + "idpGoogleClientIdDescription": "Din Google OAuth2-klient-ID", + "idpGoogleClientSecretDescription": "Google OAuth2-klienten din hemmelig", + "idpAzureConfiguration": "Azure Entra ID konfigurasjon", + "idpAzureConfigurationDescription": "Konfigurere din Azure Entra ID OAuth2 legitimasjon", + "idpTenantId": "Leietaker-ID", + "idpTenantIdPlaceholder": "din-tenant-id", + "idpAzureTenantIdDescription": "Din Azure leie-ID (funnet i Azure Active Directory-oversikten)", + "idpAzureClientIdDescription": "Din Azure App registrerings klient-ID", + "idpAzureClientSecretDescription": "Din Azure App registrerings klient hemmelig", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Google Konfigurasjon", + "idpAzureConfigurationTitle": "Azure Entra ID konfigurasjon", + "idpTenantIdLabel": "Leietaker-ID", + "idpAzureClientIdDescription2": "Din Azure App registrerings klient-ID", + "idpAzureClientSecretDescription2": "Din Azure App registrerings klient hemmelig", "idpGoogleDescription": "Google OAuth2/OIDC leverandør", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Subnett", + "subnetDescription": "Undernettverket for denne organisasjonens nettverkskonfigurasjon.", + "authPage": "Autentiseringsside", + "authPageDescription": "Konfigurer autoriseringssiden for din organisasjon", + "authPageDomain": "Autentiseringsside domene", + "noDomainSet": "Ingen domene valgt", + "changeDomain": "Endre domene", + "selectDomain": "Velg domene", + "restartCertificate": "Omstart sertifikat", + "editAuthPageDomain": "Rediger auth sidedomene", + "setAuthPageDomain": "Angi autoriseringsside domene", + "failedToFetchCertificate": "Kunne ikke hente sertifikat", + "failedToRestartCertificate": "Kan ikke starte sertifikat", + "addDomainToEnableCustomAuthPages": "Legg til et domene for å aktivere egendefinerte autentiseringssider for organisasjonen din", + "selectDomainForOrgAuthPage": "Velg et domene for organisasjonens autentiseringsside", "domainPickerProvidedDomain": "Gitt domene", "domainPickerFreeProvidedDomain": "Gratis oppgitt domene", "domainPickerVerified": "Bekreftet", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kunne ikke gjøres gyldig for {domain}.", "domainPickerSubdomainSanitized": "Underdomenet som ble sanivert", "domainPickerSubdomainCorrected": "\"{sub}\" var korrigert til \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Logg inn på din organisasjon", + "orgAuthChooseIdpDescription": "Velg din identitet leverandør for å fortsette", + "orgAuthNoIdpConfigured": "Denne organisasjonen har ikke noen identitetstjeneste konfigurert. Du kan i stedet logge inn med Pangolin identiteten din.", + "orgAuthSignInWithPangolin": "Logg inn med Pangolin", + "subscriptionRequiredToUse": "Et abonnement er påkrevd for å bruke denne funksjonen.", + "idpDisabled": "Identitetsleverandører er deaktivert.", + "orgAuthPageDisabled": "Informasjons-siden for organisasjon er deaktivert.", + "domainRestartedDescription": "Domene-verifiseringen ble startet på nytt", "resourceAddEntrypointsEditFile": "Rediger fil: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Rediger fil: docker-compose.yml", "emailVerificationRequired": "E-postbekreftelse er nødvendig. Logg inn på nytt via {dashboardUrl}/auth/login og fullfør dette trinnet. Kom deretter tilbake her.", "twoFactorSetupRequired": "To-faktor autentiseringsoppsett er nødvendig. Vennligst logg inn igjen via {dashboardUrl}/auth/login og fullfør dette steget. Kom deretter tilbake her.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", - "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "authPageErrorUpdateMessage": "Det oppstod en feil under oppdatering av innstillingene for godkjenningssiden", + "authPageUpdated": "Godkjenningsside oppdatert", + "healthCheckNotAvailable": "Lokal", + "rewritePath": "Omskriv sti", + "rewritePathDescription": "Valgfritt omskrive stien før videresending til målet." } From 033653e2343ad0c4c73b5762d68291474a4db608 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sat, 4 Oct 2025 22:17:09 -0700 Subject: [PATCH 155/322] New translations en-us.json (Spanish) --- messages/es-ES.json | 376 ++++++++++++++++++++++---------------------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/messages/es-ES.json b/messages/es-ES.json index c816ca6c..a1b92f8b 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -67,7 +67,7 @@ "siteDocker": "Expandir para detalles de despliegue de Docker", "toggle": "Cambiar", "dockerCompose": "Componer Docker", - "dockerRun": "Docker Run", + "dockerRun": "Ejecutar Docker", "siteLearnLocal": "Los sitios locales no tienen túnel, aprender más", "siteConfirmCopy": "He copiado la configuración", "searchSitesProgress": "Buscar sitios...", @@ -94,9 +94,9 @@ "siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en tu red. Sin configuración adicional.", "siteWg": "Wirex Guardia Básica", "siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.", - "siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.", + "siteWgDescriptionSaas": "Utilice cualquier cliente de WireGuard para establecer un túnel. Se requiere configuración manual de NAT. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS", "siteLocalDescription": "Solo recursos locales. Sin túneles.", - "siteLocalDescriptionSaas": "Local resources only. No tunneling.", + "siteLocalDescriptionSaas": "Solo recursos locales. Sin túneles. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS", "siteSeeAll": "Ver todos los sitios", "siteTunnelDescription": "Determina cómo quieres conectarte a tu sitio", "siteNewtCredentials": "Credenciales nuevas", @@ -159,7 +159,7 @@ "resourceHTTP": "HTTPS Recurso", "resourceHTTPDescription": "Solicitudes de proxy a tu aplicación sobre HTTPS usando un subdominio o dominio base.", "resourceRaw": "Recurso TCP/UDP sin procesar", - "resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.", + "resourceRawDescription": "Solicitudes de proxy a tu aplicación a través de TCP/UDP usando un número de puerto.", "resourceCreate": "Crear Recurso", "resourceCreateDescription": "Siga los siguientes pasos para crear un nuevo recurso", "resourceSeeAll": "Ver todos los recursos", @@ -168,9 +168,9 @@ "siteSelect": "Seleccionar sitio", "siteSearch": "Buscar sitio", "siteNotFound": "Sitio no encontrado.", - "selectCountry": "Select country", - "searchCountries": "Search countries...", - "noCountryFound": "No country found.", + "selectCountry": "Seleccionar país", + "searchCountries": "Buscar países...", + "noCountryFound": "Ningún país encontrado.", "siteSelectionDescription": "Este sitio proporcionará conectividad al objetivo.", "resourceType": "Tipo de recurso", "resourceTypeDescription": "Determina cómo quieres acceder a tu recurso", @@ -817,7 +817,7 @@ "redirectUrl": "URL de redirección", "redirectUrlAbout": "Acerca de la URL de redirección", "redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.", - "pangolinAuth": "Auth - Pangolin", + "pangolinAuth": "Autenticación - Pangolin", "verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.", "errorOccurred": "Se ha producido un error", "emailErrorVerify": "No se pudo verificar el email:", @@ -1220,7 +1220,7 @@ "billing": "Facturación", "orgBillingDescription": "Gestiona tu información de facturación y suscripciones", "github": "GitHub", - "pangolinHosted": "Pangolin Hosted", + "pangolinHosted": "Pangolin Alojado", "fossorial": "Fossorial", "completeAccountSetup": "Completar configuración de cuenta", "completeAccountSetupDescription": "Establece tu contraseña para comenzar", @@ -1258,48 +1258,48 @@ "domainPickerSubdomain": "Subdominio: {subdomain}", "domainPickerNamespace": "Espacio de nombres: {namespace}", "domainPickerShowMore": "Mostrar más", - "regionSelectorTitle": "Select Region", - "regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.", - "regionSelectorPlaceholder": "Choose a region", - "regionSelectorComingSoon": "Coming Soon", - "billingLoadingSubscription": "Loading subscription...", - "billingFreeTier": "Free Tier", - "billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.", - "billingUsageLimitsOverview": "Usage Limits Overview", - "billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.", - "billingDataUsage": "Data Usage", - "billingOnlineTime": "Site Online Time", - "billingUsers": "Active Users", - "billingDomains": "Active Domains", - "billingRemoteExitNodes": "Active Self-hosted Nodes", - "billingNoLimitConfigured": "No limit configured", - "billingEstimatedPeriod": "Estimated Billing Period", - "billingIncludedUsage": "Included Usage", - "billingIncludedUsageDescription": "Usage included with your current subscription plan", - "billingFreeTierIncludedUsage": "Free tier usage allowances", - "billingIncluded": "included", - "billingEstimatedTotal": "Estimated Total:", - "billingNotes": "Notes", - "billingEstimateNote": "This is an estimate based on your current usage.", - "billingActualChargesMayVary": "Actual charges may vary.", - "billingBilledAtEnd": "You will be billed at the end of the billing period.", - "billingModifySubscription": "Modify Subscription", - "billingStartSubscription": "Start Subscription", - "billingRecurringCharge": "Recurring Charge", - "billingManageSubscriptionSettings": "Manage your subscription settings and preferences", - "billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.", - "billingFailedToLoadSubscription": "Failed to load subscription", - "billingFailedToLoadUsage": "Failed to load usage", - "billingFailedToGetCheckoutUrl": "Failed to get checkout URL", - "billingPleaseTryAgainLater": "Please try again later.", - "billingCheckoutError": "Checkout Error", - "billingFailedToGetPortalUrl": "Failed to get portal URL", - "billingPortalError": "Portal Error", - "billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.", - "billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.", - "billingUsersInfo": "You're charged for each user in your organization. Billing is calculated daily based on the number of active user accounts in your org.", - "billingDomainInfo": "You're charged for each domain in your organization. Billing is calculated daily based on the number of active domain accounts in your org.", - "billingRemoteExitNodesInfo": "You're charged for each managed Node in your organization. Billing is calculated daily based on the number of active managed Nodes in your org.", + "regionSelectorTitle": "Seleccionar Región", + "regionSelectorInfo": "Seleccionar una región nos ayuda a brindar un mejor rendimiento para tu ubicación. No tienes que estar en la misma región que tu servidor.", + "regionSelectorPlaceholder": "Elige una región", + "regionSelectorComingSoon": "Próximamente", + "billingLoadingSubscription": "Cargando suscripción...", + "billingFreeTier": "Nivel Gratis", + "billingWarningOverLimit": "Advertencia: Has excedido uno o más límites de uso. Tus sitios no se conectarán hasta que modifiques tu suscripción o ajustes tu uso.", + "billingUsageLimitsOverview": "Descripción general de los límites de uso", + "billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@fossorial.io.", + "billingDataUsage": "Uso de datos", + "billingOnlineTime": "Tiempo en línea del sitio", + "billingUsers": "Usuarios activos", + "billingDomains": "Dominios activos", + "billingRemoteExitNodes": "Nodos autogestionados activos", + "billingNoLimitConfigured": "No se ha configurado ningún límite", + "billingEstimatedPeriod": "Período de facturación estimado", + "billingIncludedUsage": "Uso incluido", + "billingIncludedUsageDescription": "Uso incluido con su plan de suscripción actual", + "billingFreeTierIncludedUsage": "Permisos de uso del nivel gratuito", + "billingIncluded": "incluido", + "billingEstimatedTotal": "Total Estimado:", + "billingNotes": "Notas", + "billingEstimateNote": "Esta es una estimación basada en tu uso actual.", + "billingActualChargesMayVary": "Los cargos reales pueden variar.", + "billingBilledAtEnd": "Se te facturará al final del período de facturación.", + "billingModifySubscription": "Modificar Suscripción", + "billingStartSubscription": "Iniciar Suscripción", + "billingRecurringCharge": "Cargo Recurrente", + "billingManageSubscriptionSettings": "Administra la configuración y preferencias de tu suscripción", + "billingNoActiveSubscription": "No tienes una suscripción activa. Inicia tu suscripción para aumentar los límites de uso.", + "billingFailedToLoadSubscription": "Error al cargar la suscripción", + "billingFailedToLoadUsage": "Error al cargar el uso", + "billingFailedToGetCheckoutUrl": "Error al obtener la URL de pago", + "billingPleaseTryAgainLater": "Por favor, inténtelo de nuevo más tarde.", + "billingCheckoutError": "Error de pago", + "billingFailedToGetPortalUrl": "Error al obtener la URL del portal", + "billingPortalError": "Error del portal", + "billingDataUsageInfo": "Se le cobran todos los datos transferidos a través de sus túneles seguros cuando se conectan a la nube. Esto incluye tanto tráfico entrante como saliente a través de todos sus sitios. Cuando alcance su límite, sus sitios se desconectarán hasta que actualice su plan o reduzca el uso. Los datos no se cargan cuando se usan nodos.", + "billingOnlineTimeInfo": "Se te cobrará en función del tiempo que tus sitios permanezcan conectados a la nube. Por ejemplo, 44.640 minutos equivale a un sitio que funciona 24/7 durante un mes completo. Cuando alcance su límite, sus sitios se desconectarán hasta que mejore su plan o reduzca el uso. No se cargará el tiempo al usar nodos.", + "billingUsersInfo": "Se te cobra por cada usuario en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de usuario activas en tu organización.", + "billingDomainInfo": "Se te cobra por cada dominio en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de dominio activas en tu organización.", + "billingRemoteExitNodesInfo": "Se te cobra por cada nodo gestionado en tu organización. La facturación se calcula diariamente según la cantidad de nodos gestionados activos en tu organización.", "domainNotFound": "Dominio no encontrado", "domainNotFoundDescription": "Este recurso está deshabilitado porque el dominio ya no existe en nuestro sistema. Por favor, establece un nuevo dominio para este recurso.", "failed": "Fallido", @@ -1363,7 +1363,7 @@ "createDomainDnsPropagationDescription": "Los cambios de DNS pueden tardar un tiempo en propagarse a través de internet. Esto puede tardar desde unos pocos minutos hasta 48 horas, dependiendo de tu proveedor de DNS y la configuración de TTL.", "resourcePortRequired": "Se requiere número de puerto para recursos no HTTP", "resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP", - "billingPricingCalculatorLink": "Pricing Calculator", + "billingPricingCalculatorLink": "Calculadora de Precios", "signUpTerms": { "IAgreeToThe": "Estoy de acuerdo con los", "termsOfService": "términos del servicio", @@ -1412,41 +1412,41 @@ "addNewTarget": "Agregar nuevo destino", "targetsList": "Lista de destinos", "targetErrorDuplicateTargetFound": "Se encontró un destino duplicado", - "healthCheckHealthy": "Healthy", - "healthCheckUnhealthy": "Unhealthy", - "healthCheckUnknown": "Unknown", - "healthCheck": "Health Check", - "configureHealthCheck": "Configure Health Check", - "configureHealthCheckDescription": "Set up health monitoring for {target}", - "enableHealthChecks": "Enable Health Checks", - "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", - "healthScheme": "Method", - "healthSelectScheme": "Select Method", - "healthCheckPath": "Path", - "healthHostname": "IP / Host", - "healthPort": "Port", - "healthCheckPathDescription": "The path to check for health status.", - "healthyIntervalSeconds": "Healthy Interval", - "unhealthyIntervalSeconds": "Unhealthy Interval", - "IntervalSeconds": "Healthy Interval", - "timeoutSeconds": "Timeout", - "timeIsInSeconds": "Time is in seconds", - "retryAttempts": "Retry Attempts", - "expectedResponseCodes": "Expected Response Codes", - "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", + "healthCheckHealthy": "Saludable", + "healthCheckUnhealthy": "No saludable", + "healthCheckUnknown": "Desconocido", + "healthCheck": "Chequeo de salud", + "configureHealthCheck": "Configurar Chequeo de Salud", + "configureHealthCheckDescription": "Configura la monitorización de salud para {target}", + "enableHealthChecks": "Activar Chequeos de Salud", + "enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.", + "healthScheme": "Método", + "healthSelectScheme": "Seleccionar método", + "healthCheckPath": "Ruta", + "healthHostname": "IP / Nombre del host", + "healthPort": "Puerto", + "healthCheckPathDescription": "La ruta para comprobar el estado de salud.", + "healthyIntervalSeconds": "Intervalo Saludable", + "unhealthyIntervalSeconds": "Intervalo No Saludable", + "IntervalSeconds": "Intervalo Saludable", + "timeoutSeconds": "Tiempo de Espera", + "timeIsInSeconds": "El tiempo está en segundos", + "retryAttempts": "Intentos de Reintento", + "expectedResponseCodes": "Códigos de respuesta esperados", + "expectedResponseCodesDescription": "Código de estado HTTP que indica un estado saludable. Si se deja en blanco, se considera saludable de 200 a 300.", "customHeaders": "Cabeceras personalizadas", - "customHeadersDescription": "Headers new line separated: Header-Name: value", - "headersValidationError": "Headers must be in the format: Header-Name: value", - "saveHealthCheck": "Save Health Check", - "healthCheckSaved": "Health Check Saved", - "healthCheckSavedDescription": "Health check configuration has been saved successfully", - "healthCheckError": "Health Check Error", - "healthCheckErrorDescription": "An error occurred while saving the health check configuration", - "healthCheckPathRequired": "Health check path is required", - "healthCheckMethodRequired": "HTTP method is required", - "healthCheckIntervalMin": "Check interval must be at least 5 seconds", - "healthCheckTimeoutMin": "Timeout must be at least 1 second", - "healthCheckRetryMin": "Retry attempts must be at least 1", + "customHeadersDescription": "Nueva línea de cabeceras separada: Nombre de cabecera: valor", + "headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.", + "saveHealthCheck": "Guardar Chequeo de Salud", + "healthCheckSaved": "Chequeo de Salud Guardado", + "healthCheckSavedDescription": "La configuración del chequeo de salud se ha guardado correctamente", + "healthCheckError": "Error en el Chequeo de Salud", + "healthCheckErrorDescription": "Ocurrió un error al guardar la configuración del chequeo de salud", + "healthCheckPathRequired": "Se requiere la ruta del chequeo de salud", + "healthCheckMethodRequired": "Se requiere el método HTTP", + "healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos", + "healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo", + "healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1", "httpMethod": "Método HTTP", "selectHttpMethod": "Seleccionar método HTTP", "domainPickerSubdomainLabel": "Subdominio", @@ -1460,7 +1460,7 @@ "domainPickerEnterSubdomainToSearch": "Ingrese un subdominio para buscar y seleccionar entre dominios gratuitos disponibles.", "domainPickerFreeDomains": "Dominios gratuitos", "domainPickerSearchForAvailableDomains": "Buscar dominios disponibles", - "domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.", + "domainPickerNotWorkSelfHosted": "Nota: Los dominios gratuitos proporcionados no están disponibles para instancias autogestionadas por ahora.", "resourceDomain": "Dominio", "resourceEditDomain": "Editar dominio", "siteName": "Nombre del sitio", @@ -1543,72 +1543,72 @@ "autoLoginError": "Error de inicio de sesión automático", "autoLoginErrorNoRedirectUrl": "No se recibió URL de redirección del proveedor de identidad.", "autoLoginErrorGeneratingUrl": "Error al generar URL de autenticación.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", - "remoteExitNodes": "Nodes", - "searchRemoteExitNodes": "Search nodes...", - "remoteExitNodeAdd": "Add Node", - "remoteExitNodeErrorDelete": "Error deleting node", - "remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?", - "remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.", - "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", - "remoteExitNodeConfirmDelete": "Confirm Delete Node", - "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "remoteExitNodeManageRemoteExitNodes": "Administrar Nodos Autogestionados", + "remoteExitNodeDescription": "Administrar nodos para extender la conectividad de red", + "remoteExitNodes": "Nodos", + "searchRemoteExitNodes": "Buscar nodos...", + "remoteExitNodeAdd": "Añadir Nodo", + "remoteExitNodeErrorDelete": "Error al eliminar el nodo", + "remoteExitNodeQuestionRemove": "¿Está seguro de que desea eliminar el nodo {selectedNode} de la organización?", + "remoteExitNodeMessageRemove": "Una vez eliminado, el nodo ya no será accesible.", + "remoteExitNodeMessageConfirm": "Para confirmar, por favor escriba el nombre del nodo a continuación.", + "remoteExitNodeConfirmDelete": "Confirmar eliminar nodo", + "remoteExitNodeDelete": "Eliminar Nodo", + "sidebarRemoteExitNodes": "Nodos", "remoteExitNodeCreate": { - "title": "Create Node", - "description": "Create a new node to extend your network connectivity", - "viewAllButton": "View All Nodes", + "title": "Crear Nodo", + "description": "Crear un nuevo nodo para extender la conectividad de red", + "viewAllButton": "Ver todos los nodos", "strategy": { - "title": "Creation Strategy", - "description": "Choose this to manually configure your node or generate new credentials.", + "title": "Estrategia de Creación", + "description": "Elija esto para configurar manualmente su nodo o generar nuevas credenciales.", "adopt": { - "title": "Adopt Node", - "description": "Choose this if you already have the credentials for the node." + "title": "Adoptar Nodo", + "description": "Elija esto si ya tiene las credenciales para el nodo." }, "generate": { - "title": "Generate Keys", - "description": "Choose this if you want to generate new keys for the node" + "title": "Generar Claves", + "description": "Elija esto si desea generar nuevas claves para el nodo" } }, "adopt": { - "title": "Adopt Existing Node", - "description": "Enter the credentials of the existing node you want to adopt", - "nodeIdLabel": "Node ID", - "nodeIdDescription": "The ID of the existing node you want to adopt", - "secretLabel": "Secret", - "secretDescription": "The secret key of the existing node", - "submitButton": "Adopt Node" + "title": "Adoptar Nodo Existente", + "description": "Introduzca las credenciales del nodo existente que desea adoptar", + "nodeIdLabel": "ID del nodo", + "nodeIdDescription": "El ID del nodo existente que desea adoptar", + "secretLabel": "Secreto", + "secretDescription": "La clave secreta del nodo existente", + "submitButton": "Adoptar Nodo" }, "generate": { - "title": "Generated Credentials", - "description": "Use these generated credentials to configure your node", - "nodeIdTitle": "Node ID", - "secretTitle": "Secret", - "saveCredentialsTitle": "Add Credentials to Config", - "saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.", - "submitButton": "Create Node" + "title": "Credenciales Generadas", + "description": "Utilice estas credenciales generadas para configurar su nodo", + "nodeIdTitle": "ID del nodo", + "secretTitle": "Secreto", + "saveCredentialsTitle": "Agregar Credenciales a la Configuración", + "saveCredentialsDescription": "Agrega estas credenciales a tu archivo de configuración del nodo Pangolin autogestionado para completar la conexión.", + "submitButton": "Crear Nodo" }, "validation": { - "adoptRequired": "Node ID and Secret are required when adopting an existing node" + "adoptRequired": "El ID del nodo y el secreto son necesarios al adoptar un nodo existente" }, "errors": { - "loadDefaultsFailed": "Failed to load defaults", - "defaultsNotLoaded": "Defaults not loaded", - "createFailed": "Failed to create node" + "loadDefaultsFailed": "Falló al cargar los valores predeterminados", + "defaultsNotLoaded": "Valores predeterminados no cargados", + "createFailed": "Error al crear el nodo" }, "success": { - "created": "Node created successfully" + "created": "Nodo creado correctamente" } }, - "remoteExitNodeSelection": "Node Selection", - "remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site", - "remoteExitNodeRequired": "A node must be selected for local sites", - "noRemoteExitNodesAvailable": "No Nodes Available", - "noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.", - "exitNode": "Exit Node", - "country": "Country", - "rulesMatchCountry": "Currently based on source IP", + "remoteExitNodeSelection": "Selección de nodo", + "remoteExitNodeSelectionDescription": "Seleccione un nodo a través del cual enrutar el tráfico para este sitio local", + "remoteExitNodeRequired": "Un nodo debe ser seleccionado para sitios locales", + "noRemoteExitNodesAvailable": "No hay nodos disponibles", + "noRemoteExitNodesAvailableDescription": "No hay nodos disponibles para esta organización. Crea un nodo primero para usar sitios locales.", + "exitNode": "Nodo de Salida", + "country": "País", + "rulesMatchCountry": "Actualmente basado en IP de origen", "managedSelfHosted": { "title": "Autogestionado", "description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra", @@ -1647,53 +1647,53 @@ }, "internationaldomaindetected": "Dominio Internacional detectado", "willbestoredas": "Se almacenará como:", - "roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.", - "selectRole": "Select a Role", - "roleMappingExpression": "Expression", - "selectRolePlaceholder": "Choose a role", - "selectRoleDescription": "Select a role to assign to all users from this identity provider", - "roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token", - "idpTenantIdRequired": "Tenant ID is required", - "invalidValue": "Invalid value", - "idpTypeLabel": "Identity Provider Type", - "roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'", - "idpGoogleConfiguration": "Google Configuration", - "idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials", - "idpGoogleClientIdDescription": "Your Google OAuth2 Client ID", - "idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret", - "idpAzureConfiguration": "Azure Entra ID Configuration", - "idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials", - "idpTenantId": "Tenant ID", - "idpTenantIdPlaceholder": "your-tenant-id", - "idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)", - "idpAzureClientIdDescription": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription": "Your Azure App Registration Client Secret", + "roleMappingDescription": "Determinar cómo se asignan los roles a los usuarios cuando se registran cuando está habilitada la provisión automática.", + "selectRole": "Seleccione un rol", + "roleMappingExpression": "Expresión", + "selectRolePlaceholder": "Elija un rol", + "selectRoleDescription": "Seleccione un rol para asignar a todos los usuarios de este proveedor de identidad", + "roleMappingExpressionDescription": "Introduzca una expresión JMESPath para extraer información de rol del token de ID", + "idpTenantIdRequired": "El ID del cliente es obligatorio", + "invalidValue": "Valor inválido", + "idpTypeLabel": "Tipo de proveedor de identidad", + "roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'", + "idpGoogleConfiguration": "Configuración de Google", + "idpGoogleConfigurationDescription": "Configura tus credenciales de Google OAuth2", + "idpGoogleClientIdDescription": "Tu ID de cliente de Google OAuth2", + "idpGoogleClientSecretDescription": "Tu secreto de cliente de Google OAuth2", + "idpAzureConfiguration": "Configuración de Azure Entra ID", + "idpAzureConfigurationDescription": "Configure sus credenciales de Azure Entra ID OAuth2", + "idpTenantId": "ID del inquilino", + "idpTenantIdPlaceholder": "su-inquilino-id", + "idpAzureTenantIdDescription": "Su ID de inquilino de Azure (encontrado en el resumen de Azure Active Directory)", + "idpAzureClientIdDescription": "Tu ID de Cliente de Registro de Azure App", + "idpAzureClientSecretDescription": "Tu Azure App Registro Cliente secreto", "idpGoogleTitle": "Google", "idpGoogleAlt": "Google", "idpAzureTitle": "Azure Entra ID", "idpAzureAlt": "Azure", - "idpGoogleConfigurationTitle": "Google Configuration", - "idpAzureConfigurationTitle": "Azure Entra ID Configuration", - "idpTenantIdLabel": "Tenant ID", - "idpAzureClientIdDescription2": "Your Azure App Registration Client ID", - "idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret", + "idpGoogleConfigurationTitle": "Configuración de Google", + "idpAzureConfigurationTitle": "Configuración de Azure Entra ID", + "idpTenantIdLabel": "ID del inquilino", + "idpAzureClientIdDescription2": "Tu ID de Cliente de Registro de Azure App", + "idpAzureClientSecretDescription2": "Tu Azure App Registro Cliente secreto", "idpGoogleDescription": "Proveedor OAuth2/OIDC de Google", "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", - "subnet": "Subnet", - "subnetDescription": "The subnet for this organization's network configuration.", - "authPage": "Auth Page", - "authPageDescription": "Configure the auth page for your organization", - "authPageDomain": "Auth Page Domain", - "noDomainSet": "No domain set", - "changeDomain": "Change Domain", - "selectDomain": "Select Domain", - "restartCertificate": "Restart Certificate", - "editAuthPageDomain": "Edit Auth Page Domain", - "setAuthPageDomain": "Set Auth Page Domain", - "failedToFetchCertificate": "Failed to fetch certificate", - "failedToRestartCertificate": "Failed to restart certificate", - "addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization", - "selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page", + "subnet": "Subred", + "subnetDescription": "La subred para la configuración de red de esta organización.", + "authPage": "Página Auth", + "authPageDescription": "Configurar la página de autenticación de su organización", + "authPageDomain": "Dominio de la página Auth", + "noDomainSet": "Ningún dominio establecido", + "changeDomain": "Cambiar dominio", + "selectDomain": "Seleccionar dominio", + "restartCertificate": "Reiniciar certificado", + "editAuthPageDomain": "Editar dominio Auth Page", + "setAuthPageDomain": "Establecer dominio Auth Page", + "failedToFetchCertificate": "Error al obtener el certificado", + "failedToRestartCertificate": "Error al reiniciar el certificado", + "addDomainToEnableCustomAuthPages": "Añadir un dominio para habilitar páginas de autenticación personalizadas para su organización", + "selectDomainForOrgAuthPage": "Seleccione un dominio para la página de autenticación de la organización", "domainPickerProvidedDomain": "Dominio proporcionado", "domainPickerFreeProvidedDomain": "Dominio proporcionado gratis", "domainPickerVerified": "Verificado", @@ -1707,21 +1707,21 @@ "domainPickerInvalidSubdomainCannotMakeValid": "No se ha podido hacer válido \"{sub}\" para {domain}.", "domainPickerSubdomainSanitized": "Subdominio saneado", "domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"", - "orgAuthSignInTitle": "Sign in to your organization", - "orgAuthChooseIdpDescription": "Choose your identity provider to continue", - "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", - "orgAuthSignInWithPangolin": "Sign in with Pangolin", - "subscriptionRequiredToUse": "A subscription is required to use this feature.", - "idpDisabled": "Identity providers are disabled.", - "orgAuthPageDisabled": "Organization auth page is disabled.", - "domainRestartedDescription": "Domain verification restarted successfully", + "orgAuthSignInTitle": "Inicia sesión en tu organización", + "orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar", + "orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.", + "orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin", + "subscriptionRequiredToUse": "Se requiere una suscripción para utilizar esta función.", + "idpDisabled": "Los proveedores de identidad están deshabilitados.", + "orgAuthPageDisabled": "La página de autenticación de la organización está deshabilitada.", + "domainRestartedDescription": "Verificación de dominio reiniciada con éxito", "resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml", "resourceExposePortsEditFile": "Editar archivo: docker-compose.yml", "emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.", "twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí.", - "authPageErrorUpdateMessage": "An error occurred while updating the auth page settings", - "authPageUpdated": "Auth page updated successfully", + "authPageErrorUpdateMessage": "Ocurrió un error mientras se actualizaban los ajustes de la página auth", + "authPageUpdated": "Página auth actualizada correctamente", "healthCheckNotAvailable": "Local", - "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "rewritePath": "Reescribir Ruta", + "rewritePathDescription": "Opcionalmente reescribe la ruta antes de reenviar al destino." } From 076912c6486f79badce5402931a13ccbe42cfa16 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 5 Oct 2025 10:26:02 -0700 Subject: [PATCH 156/322] fix hostname in set cookie --- src/actions/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/server.ts b/src/actions/server.ts index 655cbc63..e2b2d018 100644 --- a/src/actions/server.ts +++ b/src/actions/server.ts @@ -57,7 +57,7 @@ function parseSetCookieString( } if (!options.domain) { - const d = host ? host : new URL(env.app.dashboardUrl).hostname; + const d = host ? new URL(env.app.dashboardUrl).hostname : undefined; if (d) { options.domain = d; } From 84fe2fb92eea31ec0a073234c33b4e242c505bf2 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 5 Oct 2025 10:36:29 -0700 Subject: [PATCH 157/322] Remove domains from price --- server/lib/private/billing/features.ts | 8 +++++--- server/lib/private/billing/limitSet.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/server/lib/private/billing/features.ts b/server/lib/private/billing/features.ts index 316d8e86..11d78bbb 100644 --- a/server/lib/private/billing/features.ts +++ b/server/lib/private/billing/features.ts @@ -51,14 +51,16 @@ export function getFeatureIdByMetricId(metricId: string): FeatureId | undefined } export type FeaturePriceSet = { - [key in FeatureId]: string; + [key in Exclude]: string; +} & { + [FeatureId.DOMAINS]?: string; // Optional since domains are not billed }; export const standardFeaturePriceSet: FeaturePriceSet = { // Free tier matches the freeLimitSet [FeatureId.SITE_UPTIME]: "price_1RrQc4D3Ee2Ir7WmaJGZ3MtF", [FeatureId.USERS]: "price_1RrQeJD3Ee2Ir7WmgveP3xea", [FeatureId.EGRESS_DATA_MB]: "price_1RrQXFD3Ee2Ir7WmvGDlgxQk", - [FeatureId.DOMAINS]: "price_1Rz3tMD3Ee2Ir7Wm5qLeASzC", + // [FeatureId.DOMAINS]: "price_1Rz3tMD3Ee2Ir7Wm5qLeASzC", [FeatureId.REMOTE_EXIT_NODES]: "price_1S46weD3Ee2Ir7Wm94KEHI4h" }; @@ -66,7 +68,7 @@ export const standardFeaturePriceSetSandbox: FeaturePriceSet = { // Free tier ma [FeatureId.SITE_UPTIME]: "price_1RefFBDCpkOb237BPrKZ8IEU", [FeatureId.USERS]: "price_1ReNa4DCpkOb237Bc67G5muF", [FeatureId.EGRESS_DATA_MB]: "price_1Rfp9LDCpkOb237BwuN5Oiu0", - [FeatureId.DOMAINS]: "price_1Ryi88DCpkOb237B2D6DM80b", + // [FeatureId.DOMAINS]: "price_1Ryi88DCpkOb237B2D6DM80b", [FeatureId.REMOTE_EXIT_NODES]: "price_1RyiZvDCpkOb237BXpmoIYJL" }; diff --git a/server/lib/private/billing/limitSet.ts b/server/lib/private/billing/limitSet.ts index 0cddb37c..ec6107b2 100644 --- a/server/lib/private/billing/limitSet.ts +++ b/server/lib/private/billing/limitSet.ts @@ -53,7 +53,7 @@ export const subscribedLimitSet: LimitSet = { description: "Contact us to increase soft limit." }, // 12000 GB [FeatureId.DOMAINS]: { - value: 20, + value: 25, description: "Contact us to increase soft limit." }, [FeatureId.REMOTE_EXIT_NODES]: { From e4c47c46a6f5fbb211d821d6a204103f0295cdcc Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 5 Oct 2025 10:48:34 -0700 Subject: [PATCH 158/322] Restore npm ci and add tzdata --- Dockerfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index a48e4a1a..3e37e828 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,14 +25,12 @@ FROM node:22-alpine AS runner WORKDIR /app # Curl used for the health checks -RUN apk add --no-cache curl +RUN apk add --no-cache curl tzdata # COPY package.json package-lock.json ./ COPY package*.json ./ -# 'npm ci --omit=dev' with 'npm install --omit=dev' to fix Alpine build failure -# RUN npm ci --omit=dev && npm cache clean --force -RUN npm install --omit=dev && npm cache clean --force +RUN npm ci --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static From b47fc9f901952c624b5cae90bb82b3d2689fea5e Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 21:21:42 -0700 Subject: [PATCH 159/322] frontend for ordered priority --- .../resources/[niceId]/proxy/page.tsx | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index a4277f6b..f27022e0 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -74,7 +74,10 @@ import { CircleX, ArrowRight, Plus, - MoveRight + MoveRight, + ArrowUp, + Info, + ArrowDown } from "lucide-react"; import { ContainersSelector } from "@app/components/ContainersSelector"; import { useTranslations } from "next-intl"; @@ -106,6 +109,7 @@ import { PathRewriteModal } from "@app/components/PathMatchRenameModal"; import { Badge } from "@app/components/ui/badge"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; const addTargetSchema = z .object({ @@ -660,6 +664,47 @@ export default function ReverseProxyTargets(props: { } const columns: ColumnDef[] = [ + { + id: "priority", + header: () => ( +
+ Priority + + + + + + +

Higher priority routes are evaluated first. Use this to ensure specific paths like /api/v1 are checked before catch-all routes like /

+
+
+
+
+ ), + cell: ({ row }) => { + const targetIndex = targets.findIndex(t => t.targetId === row.original.targetId); + return ( +
+ { + const value = parseInt(e.target.value, 10); + if (value >= 1 && value <= 1000) { + updateTarget(row.original.targetId, { + ...row.original, + //priority: value + }); + } + }} + /> +
+ ); + } + }, { accessorKey: "path", header: t("matchPath"), From ff2bcfb0e7b4754f04610be4a4710e2296a5e063 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 21:25:31 -0700 Subject: [PATCH 160/322] backend setup --- server/db/pg/schema.ts | 3 +- server/db/sqlite/schema.ts | 3 +- server/routers/target/createTarget.ts | 3 +- server/routers/target/listTargets.ts | 3 +- server/routers/target/updateTarget.ts | 3 +- .../resources/[niceId]/proxy/page.tsx | 6 +-- .../settings/resources/create/page.tsx | 44 ++++++++++++++++++- 7 files changed, 56 insertions(+), 9 deletions(-) diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 29c14560..764e343d 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -125,7 +125,8 @@ export const targets = pgTable("targets", { path: text("path"), pathMatchType: text("pathMatchType"), // exact, prefix, regex rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target - rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix + rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix + priority: integer("priority").notNull().default(100) }); export const targetHealthCheck = pgTable("targetHealthCheck", { diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 62fca8b4..21e44a92 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -137,7 +137,8 @@ export const targets = sqliteTable("targets", { path: text("path"), pathMatchType: text("pathMatchType"), // exact, prefix, regex rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target - rewritePathType: text("rewritePathType") // exact, prefix, regex, stripPrefix + rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix + priority: integer("priority").notNull().default(100) }); export const targetHealthCheck = sqliteTable("targetHealthCheck", { diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 0b473563..d29d5f7d 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -53,7 +53,8 @@ const createTargetSchema = z path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), + priority: z.number().int().min(1).max(1000).default(100) }) .strict(); diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 178ec967..04966f6e 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -62,7 +62,8 @@ function queryTargets(resourceId: number) { path: targets.path, pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, - rewritePathType: targets.rewritePathType + rewritePathType: targets.rewritePathType, + priority: targets.priority, }) .from(targets) .leftJoin(sites, eq(sites.siteId, targets.siteId)) diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index af629729..e7794b32 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -50,7 +50,8 @@ const updateTargetBodySchema = z path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), + priority: z.number().int().min(1).max(1000).default(100) }) .strict() .refine((data) => Object.keys(data).length > 0, { diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index f27022e0..c4068741 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -489,6 +489,7 @@ export default function ReverseProxyTargets(props: { targetId: new Date().getTime(), new: true, resourceId: resource.resourceId, + priority: 100, hcEnabled: false, hcPath: null, hcMethod: null, @@ -682,21 +683,20 @@ export default function ReverseProxyTargets(props: {
), cell: ({ row }) => { - const targetIndex = targets.findIndex(t => t.targetId === row.original.targetId); return (
{ const value = parseInt(e.target.value, 10); if (value >= 1 && value <= 1000) { updateTarget(row.original.targetId, { ...row.original, - //priority: value + priority: value }); } }} diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 1810f09e..80eb5da1 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -58,7 +58,7 @@ import { } from "@app/components/ui/popover"; import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"; import { cn } from "@app/lib/cn"; -import { ArrowRight, MoveRight, Plus, SquareArrowOutUpRight } from "lucide-react"; +import { ArrowRight, Info, MoveRight, Plus, SquareArrowOutUpRight } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import Link from "next/link"; import { useTranslations } from "next-intl"; @@ -92,6 +92,7 @@ import { parseHostTarget } from "@app/lib/parseHostTarget"; import { toASCII, toUnicode } from 'punycode'; import { DomainRow } from "../../../../../components/DomainsTable"; import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; import { PathMatchDisplay, PathMatchModal, PathRewriteDisplay, PathRewriteModal } from "@app/components/PathMatchRenameModal"; @@ -341,6 +342,7 @@ export default function Page() { targetId: new Date().getTime(), new: true, resourceId: 0, // Will be set when resource is created + priority: 100, // Default priority hcEnabled: false, hcPath: null, hcMethod: null, @@ -598,6 +600,46 @@ export default function Page() { }, []); const columns: ColumnDef[] = [ + { + id: "priority", + header: () => ( +
+ Priority + + + + + + +

Higher priority routes are evaluated first. Use this to ensure specific paths like /api/v1 are checked before catch-all routes like /

+
+
+
+
+ ), + cell: ({ row }) => { + return ( +
+ { + const value = parseInt(e.target.value, 10); + if (value >= 1 && value <= 1000) { + updateTarget(row.original.targetId, { + ...row.original, + priority: value + }); + } + }} + /> +
+ ); + } + }, { accessorKey: "path", header: t("matchPath"), From 1e4ca69c89171965b5590edf583fc5a218a6c6ef Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 4 Oct 2025 21:29:59 -0700 Subject: [PATCH 161/322] priority add for traefik config setup --- server/lib/blueprints/proxyResources.ts | 8 +++ server/lib/blueprints/types.ts | 3 +- server/lib/traefik/privateGetTraefikConfig.ts | 56 ++++++++++++++----- server/routers/target/createTarget.ts | 4 +- server/routers/target/updateTarget.ts | 4 +- .../resources/[niceId]/proxy/page.tsx | 11 ++-- .../settings/resources/create/page.tsx | 10 +++- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index e6525191..c142cdc0 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -113,8 +113,12 @@ export async function updateProxyResources( internalPort: internalPortToCreate, path: targetData.path, pathMatchType: targetData["path-match"], +<<<<<<< HEAD rewritePath: targetData.rewritePath, rewritePathType: targetData["rewrite-match"] +======= + priority: targetData.priority +>>>>>>> b8d96345 (priority add for traefik config setup) }) .returning(); @@ -362,8 +366,12 @@ export async function updateProxyResources( enabled: targetData.enabled, path: targetData.path, pathMatchType: targetData["path-match"], +<<<<<<< HEAD rewritePath: targetData.rewritePath, rewritePathType: targetData["rewrite-match"] +======= + priority: targetData.priority +>>>>>>> b8d96345 (priority add for traefik config setup) }) .where(eq(targets.targetId, existingTarget.targetId)) .returning(); diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index 54105dde..bc152d57 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -33,7 +33,8 @@ export const TargetSchema = z.object({ "path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(), healthcheck: TargetHealthCheckSchema.optional(), rewritePath: z.string().optional(), - "rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() + "rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), + priority: z.number().int().min(1).max(1000).optional().default(100) }); export type TargetData = z.infer; diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts index 7f1ff614..f8e7b8b5 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -20,7 +20,7 @@ import { loginPage, targetHealthCheck } from "@server/db"; -import { and, eq, inArray, or, isNull, ne, isNotNull } from "drizzle-orm"; +import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; @@ -77,7 +77,8 @@ export async function getTraefikConfig( hcHealth: targetHealthCheck.hcHealth, path: targets.path, pathMatchType: targets.pathMatchType, - + priority: targets.priority, + // Site fields siteId: sites.siteId, siteType: sites.type, @@ -118,7 +119,8 @@ export async function getTraefikConfig( ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true : eq(resources.http, true) ) - ); + ) + .orderBy(desc(targets.priority), targets.targetId); // stable ordering // Group by resource and include targets with their unique site data const resourcesMap = new Map(); @@ -127,6 +129,7 @@ export async function getTraefikConfig( const resourceId = row.resourceId; const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; + const priority = row.priority ?? 100; if (filterOutNamespaceDomains && row.domainNamespaceId) { return; @@ -155,7 +158,8 @@ export async function getTraefikConfig( targets: [], headers: row.headers, path: row.path, // the targets will all have the same path - pathMatchType: row.pathMatchType // the targets will all have the same pathMatchType + pathMatchType: row.pathMatchType, // the targets will all have the same pathMatchType + priority: priority // may be null, we fallback later }); } @@ -168,6 +172,7 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, + priority: row.priority, site: { siteId: row.siteId, type: row.siteType, @@ -331,9 +336,30 @@ export async function getTraefikConfig( } let rule = `Host(\`${fullDomain}\`)`; - let priority = 100; + + // priority logic + let priority: number; + if (resource.priority && resource.priority != 100) { + priority = resource.priority; + } else { + priority = 100; + if (resource.path && resource.pathMatchType) { + priority += 10; + if (resource.pathMatchType === "exact") { + priority += 5; + } else if (resource.pathMatchType === "prefix") { + priority += 3; + } else if (resource.pathMatchType === "regex") { + priority += 2; + } + if (resource.path === "/") { + priority = 1; // lowest for catch-all + } + } + } + if (resource.path && resource.pathMatchType) { - priority += 1; + //priority += 1; // add path to rule based on match type let path = resource.path; // if the path doesn't start with a /, add it @@ -389,7 +415,7 @@ export async function getTraefikConfig( return ( (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + .filter((target: TargetWithSite) => { if (!target.enabled) { return false; } @@ -410,7 +436,7 @@ export async function getTraefikConfig( ) { return false; } - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { if ( !target.internalPort || !target.method || @@ -418,10 +444,10 @@ export async function getTraefikConfig( ) { return false; } - } - return true; - }) - .map((target: TargetWithSite) => { + } + return true; + }) + .map((target: TargetWithSite) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -429,14 +455,14 @@ export async function getTraefikConfig( return { url: `${target.method}://${target.ip}:${target.port}` }; - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; return { url: `${target.method}://${ip}:${target.internalPort}` }; - } - }) + } + }) // filter out duplicates .filter( (v, i, a) => diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index d29d5f7d..46dd3916 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -52,9 +52,7 @@ const createTargetSchema = z hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), - rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), - priority: z.number().int().min(1).max(1000).default(100) + priority: z.number().int().min(1).max(1000) }) .strict(); diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index e7794b32..5e111f0d 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -49,9 +49,7 @@ const updateTargetBodySchema = z hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), - rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), - priority: z.number().int().min(1).max(1000).default(100) + priority: z.number().int().min(1).max(1000).optional(), }) .strict() .refine((data) => Object.keys(data).length > 0, { diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index c4068741..7df76cb5 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -305,7 +305,8 @@ export default function ReverseProxyTargets(props: { path: null, pathMatchType: null, rewritePath: null, - rewritePathType: null + rewritePathType: null, + priority: 100 } as z.infer }); @@ -514,7 +515,8 @@ export default function ReverseProxyTargets(props: { path: null, pathMatchType: null, rewritePath: null, - rewritePathType: null + rewritePathType: null, + priority: 100, }); } @@ -592,7 +594,8 @@ export default function ReverseProxyTargets(props: { path: target.path, pathMatchType: target.pathMatchType, rewritePath: target.rewritePath, - rewritePathType: target.rewritePathType + rewritePathType: target.rewritePathType, + priority: target.priority }; if (target.new) { @@ -676,7 +679,7 @@ export default function ReverseProxyTargets(props: { -

Higher priority routes are evaluated first. Use this to ensure specific paths like /api/v1 are checked before catch-all routes like /

+

Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.

diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 80eb5da1..55a7a7be 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -120,7 +120,8 @@ const addTargetSchema = z.object({ path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable() + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), + priority: z.number().int().min(1).max(1000) }).refine( (data) => { // If path is provided, pathMatchType must be provided @@ -263,6 +264,7 @@ export default function Page() { pathMatchType: null, rewritePath: null, rewritePathType: null, + priority: 100, } as z.infer }); @@ -368,6 +370,7 @@ export default function Page() { pathMatchType: null, rewritePath: null, rewritePathType: null, + priority: 100, }); } @@ -477,7 +480,8 @@ export default function Page() { path: target.path, pathMatchType: target.pathMatchType, rewritePath: target.rewritePath, - rewritePathType: target.rewritePathType + rewritePathType: target.rewritePathType, + priority: target.priority }; await api.put(`/resource/${id}/target`, data); @@ -611,7 +615,7 @@ export default function Page() { -

Higher priority routes are evaluated first. Use this to ensure specific paths like /api/v1 are checked before catch-all routes like /

+

Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.

From 043834274d89663f11e97c545ff9ec74ff975a32 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 6 Oct 2025 01:22:23 +0530 Subject: [PATCH 162/322] fix priority inside blueprints --- server/lib/blueprints/proxyResources.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index c142cdc0..bbfc260f 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -113,12 +113,9 @@ export async function updateProxyResources( internalPort: internalPortToCreate, path: targetData.path, pathMatchType: targetData["path-match"], -<<<<<<< HEAD rewritePath: targetData.rewritePath, - rewritePathType: targetData["rewrite-match"] -======= + rewritePathType: targetData["rewrite-match"], priority: targetData.priority ->>>>>>> b8d96345 (priority add for traefik config setup) }) .returning(); @@ -366,12 +363,9 @@ export async function updateProxyResources( enabled: targetData.enabled, path: targetData.path, pathMatchType: targetData["path-match"], -<<<<<<< HEAD rewritePath: targetData.rewritePath, - rewritePathType: targetData["rewrite-match"] -======= + rewritePathType: targetData["rewrite-match"], priority: targetData.priority ->>>>>>> b8d96345 (priority add for traefik config setup) }) .where(eq(targets.targetId, existingTarget.targetId)) .returning(); From b6c76a21641a887ee3169266d06297724e05edde Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 6 Oct 2025 01:37:33 +0530 Subject: [PATCH 163/322] add priority type --- src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 7df76cb5..302d16d2 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -126,7 +126,8 @@ const addTargetSchema = z rewritePathType: z .enum(["exact", "prefix", "regex", "stripPrefix"]) .optional() - .nullable() + .nullable(), + priority: z.number().int().min(1).max(1000) }) .refine( (data) => { From 22477b7e8161930da120d6ee98c8720eb41b101c Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 6 Oct 2025 02:16:06 +0530 Subject: [PATCH 164/322] add removed rewrite schema --- server/routers/target/createTarget.ts | 2 ++ server/routers/target/updateTarget.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 46dd3916..d5be025b 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -52,6 +52,8 @@ const createTargetSchema = z hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), priority: z.number().int().min(1).max(1000) }) .strict(); diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 5e111f0d..d66c7cd0 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -49,6 +49,8 @@ const updateTargetBodySchema = z hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + rewritePath: z.string().optional().nullable(), + rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), priority: z.number().int().min(1).max(1000).optional(), }) .strict() From 9649d9a46b5589931f9d9585fb646d0d6623e250 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 5 Oct 2025 14:59:34 -0700 Subject: [PATCH 165/322] fix redirect issue in firefox and safari --- messages/en-US.json | 3 +- src/actions/server.ts | 8 +- src/app/auth/(private)/org/page.tsx | 8 +- src/components/private/IdpLoginButtons.tsx | 110 ++++++++++++++------- 4 files changed, 87 insertions(+), 42 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 6783e974..ca5557bc 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1724,5 +1724,6 @@ "authPageUpdated": "Auth page updated successfully", "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", - "rewritePathDescription": "Optionally rewrite the path before forwarding to the target." + "rewritePathDescription": "Optionally rewrite the path before forwarding to the target.", + "continueToApplication": "Continue to application" } diff --git a/src/actions/server.ts b/src/actions/server.ts index e2b2d018..a6ccff8c 100644 --- a/src/actions/server.ts +++ b/src/actions/server.ts @@ -57,9 +57,12 @@ function parseSetCookieString( } if (!options.domain) { - const d = host ? new URL(env.app.dashboardUrl).hostname : undefined; + const d = host + ? host.split(":")[0] // strip port if present + : new URL(env.app.dashboardUrl).hostname; if (d) { options.domain = d; + console.log("Setting cookie domain to:", d); } } @@ -91,7 +94,8 @@ async function makeApiRequest( res = await fetch(url, { method, headers, - body: body ? JSON.stringify(body) : undefined + body: body ? JSON.stringify(body) : undefined, + cache: "no-store" }); } catch (fetchError) { console.error("API request failed:", fetchError); diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index f9d1854e..0cbe101b 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -69,6 +69,8 @@ export default async function OrgAuthPage(props: { const t = await getTranslations(); const expectedHost = env.app.dashboardUrl.split("//")[1]; + + let redirectToUrl: string | undefined; let loginPage: LoadLoginPageResponse | undefined; if (host !== expectedHost) { try { @@ -110,7 +112,6 @@ export default async function OrgAuthPage(props: { if (res && res.status === 200) { const newToken = res.data.data.token; - redirectToken = newToken; } } catch (e) { @@ -120,9 +121,8 @@ export default async function OrgAuthPage(props: { } if (redirectToken) { - redirect( - `${env.app.dashboardUrl}/auth/org?token=${redirectToken}` - ); + redirectToUrl = `${env.app.dashboardUrl}/auth/org?token=${redirectToken}`; + redirect(redirectToUrl); } } } else { diff --git a/src/components/private/IdpLoginButtons.tsx b/src/components/private/IdpLoginButtons.tsx index 95e9a9f3..6b3a2e0b 100644 --- a/src/components/private/IdpLoginButtons.tsx +++ b/src/components/private/IdpLoginButtons.tsx @@ -13,13 +13,21 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Button } from "@app/components/ui/button"; import { Alert, AlertDescription } from "@app/components/ui/alert"; import { useTranslations } from "next-intl"; import Image from "next/image"; -import { generateOidcUrlProxy, type GenerateOidcUrlResponse } from "@app/actions/server"; -import { redirect as redirectTo } from "next/navigation"; +import { + generateOidcUrlProxy, + type GenerateOidcUrlResponse +} from "@app/actions/server"; +import { + redirect as redirectTo, + useParams, + useSearchParams +} from "next/navigation"; +import { useRouter } from "next/navigation"; export type LoginFormIDP = { idpId: number; @@ -42,6 +50,20 @@ export default function IdpLoginButtons({ const [loading, setLoading] = useState(false); const t = useTranslations(); + const params = useSearchParams(); + const router = useRouter(); + + function goToApp() { + const url = window.location.href.split("?")[0]; + router.push(url); + } + + useEffect(() => { + if (params.get("gotoapp")) { + goToApp(); + } + }, []); + async function loginWithIdp(idpId: number) { setLoading(true); setError(null); @@ -50,7 +72,7 @@ export default function IdpLoginButtons({ try { const response = await generateOidcUrlProxy( idpId, - redirect || "/", + redirect || "/auth/org?gotoapp=app", orgId ); @@ -69,7 +91,8 @@ export default function IdpLoginButtons({ console.error(e); setError( t("loginError", { - defaultValue: "An unexpected error occurred. Please try again." + defaultValue: + "An unexpected error occurred. Please try again." }) ); setLoading(false); @@ -93,42 +116,59 @@ export default function IdpLoginButtons({ )}
- {idps.map((idp) => { - const effectiveType = idp.variant || idp.name.toLowerCase(); - - return ( + {params.get("gotoapp") ? ( + <> - ); - })} + + ) : ( + <> + {idps.map((idp) => { + const effectiveType = + idp.variant || idp.name.toLowerCase(); + + return ( + + ); + })} + + )}
); From e5f4da9a99698f94be56a0bb3c0550c5fce2f4e9 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 5 Oct 2025 15:22:54 -0700 Subject: [PATCH 166/322] Fix lint errors --- server/db/countries.ts | 2 +- server/db/private/redis.ts | 2 +- server/lib/blueprints/proxyResources.ts | 4 ++-- server/lib/private/billing/usageService.ts | 2 +- server/lib/private/readConfigFile.ts | 2 +- server/lib/private/resend.ts | 2 +- server/lib/traefik/middleware.ts | 2 +- server/routers/auth/privateQuickStart.ts | 6 ++---- server/routers/newt/handleNewtRegisterMessage.ts | 4 ++-- server/routers/newt/targets.ts | 2 +- server/routers/private/billing/getOrgUsage.ts | 12 ++++++------ server/routers/private/hybrid.ts | 2 +- server/routers/ws/privateWs.ts | 4 ++-- src/components/PermissionsSelectBox.tsx | 2 +- 14 files changed, 23 insertions(+), 25 deletions(-) diff --git a/server/db/countries.ts b/server/db/countries.ts index 52eb2b4b..2907fd69 100644 --- a/server/db/countries.ts +++ b/server/db/countries.ts @@ -1011,4 +1011,4 @@ export const COUNTRIES = [ "name": "Åland Islands", "code": "AX" } -] \ No newline at end of file +]; \ No newline at end of file diff --git a/server/db/private/redis.ts b/server/db/private/redis.ts index b1f01a49..d6e67262 100644 --- a/server/db/private/redis.ts +++ b/server/db/private/redis.ts @@ -44,7 +44,7 @@ class RedisManager { constructor() { if (build == "oss") { this.isEnabled = false; - return + return; } this.isEnabled = config.getRawPrivateConfig().flags?.enable_redis || false; if (this.isEnabled) { diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index e6525191..adeb320f 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -47,8 +47,8 @@ export async function updateProxyResources( for (const [resourceNiceId, resourceData] of Object.entries( config["proxy-resources"] )) { - let targetsToUpdate: Target[] = []; - let healthchecksToUpdate: TargetHealthCheck[] = []; + const targetsToUpdate: Target[] = []; + const healthchecksToUpdate: TargetHealthCheck[] = []; let resource: Resource; async function createTarget( // reusable function to create a target diff --git a/server/lib/private/billing/usageService.ts b/server/lib/private/billing/usageService.ts index 76099956..f18542d2 100644 --- a/server/lib/private/billing/usageService.ts +++ b/server/lib/private/billing/usageService.ts @@ -651,7 +651,7 @@ export class UsageService { } } - return result + return result; } catch (error) { logger.error( `Failed to get usage for ${orgId}/${featureId}:`, diff --git a/server/lib/private/readConfigFile.ts b/server/lib/private/readConfigFile.ts index ce2abcf4..ddc9ee82 100644 --- a/server/lib/private/readConfigFile.ts +++ b/server/lib/private/readConfigFile.ts @@ -155,7 +155,7 @@ export const privateConfigSchema = z localFilePath: z.string() }) .optional(), - }) + }); export function readPrivateConfigFile() { if (build == "oss") { diff --git a/server/lib/private/resend.ts b/server/lib/private/resend.ts index a502e98c..26c3f4a6 100644 --- a/server/lib/private/resend.ts +++ b/server/lib/private/resend.ts @@ -59,7 +59,7 @@ export async function moveEmailToAudience( if (data) { logger.debug( `Added email ${email} to audience ${audienceId} with contact ID ${data.id}` - ) + ); } const otherAudiences = Object.values(AudienceIds).filter( diff --git a/server/lib/traefik/middleware.ts b/server/lib/traefik/middleware.ts index e6660776..e4055976 100644 --- a/server/lib/traefik/middleware.ts +++ b/server/lib/traefik/middleware.ts @@ -24,7 +24,7 @@ export default function createPathRewriteMiddleware( switch (rewritePathType) { case "exact": // Replace the path with the exact rewrite path - let exactPattern = `^${escapeRegex(path)}$`; + const exactPattern = `^${escapeRegex(path)}$`; middlewares[middlewareName] = { replacePathRegex: { regex: exactPattern, diff --git a/server/routers/auth/privateQuickStart.ts b/server/routers/auth/privateQuickStart.ts index bdb95e5b..683c24a8 100644 --- a/server/routers/auth/privateQuickStart.ts +++ b/server/routers/auth/privateQuickStart.ts @@ -183,7 +183,6 @@ export async function quickStart( let secret: string; let fullDomain: string; let resource: Resource; - let orgId: string; let completeSignUpLink: string; await db.transaction(async (trx) => { @@ -216,7 +215,7 @@ export async function quickStart( throw new Error("Failed to create user account and organization"); } - orgId = org.orgId; + const orgId = org.orgId; await db.transaction(async (trx) => { const token = generateRandomString( @@ -251,7 +250,6 @@ export async function quickStart( // Create the sandbox site const siteNiceId = await getUniqueSiteName(orgId); const siteName = `First Site`; - let siteId: number | undefined; // pick a random exit node const exitNodesList = await listExitNodes(orgId); @@ -277,7 +275,7 @@ export async function quickStart( }) .returning(); - siteId = newSite.siteId; + const siteId = newSite.siteId; const adminRole = await trx .select() diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 51a1ab05..021527ad 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -28,7 +28,7 @@ export type ExitNodePingResult = { wasPreviouslyConnected: boolean; }; -let numTimesLimitExceededForId: Record = {}; +const numTimesLimitExceededForId: Record = {}; export const handleNewtRegisterMessage: MessageHandler = async (context) => { const { message, client, sendToClient } = context; @@ -323,7 +323,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { const hcHeadersParse = target.hcHeaders ? JSON.parse(target.hcHeaders) : null; - let hcHeadersSend: { [key: string]: string } = {}; + const hcHeadersSend: { [key: string]: string } = {}; if (hcHeadersParse) { hcHeadersParse.forEach( (header: { name: string; value: string }) => { diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index c05a64a1..a886b00b 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -102,7 +102,7 @@ export async function removeTargets( }); const healthCheckTargets = targets.map((target) => { - return target.targetId + return target.targetId; }); await sendToClient(newtId, { diff --git a/server/routers/private/billing/getOrgUsage.ts b/server/routers/private/billing/getOrgUsage.ts index b387fd52..e1544e06 100644 --- a/server/routers/private/billing/getOrgUsage.ts +++ b/server/routers/private/billing/getOrgUsage.ts @@ -82,13 +82,13 @@ export async function getOrgUsage( } // Get usage for org - let usageData = []; + const usageData = []; - const siteUptime = await usageService.getUsage(orgId, FeatureId.SITE_UPTIME) - const users = await usageService.getUsageDaily(orgId, FeatureId.USERS) - const domains = await usageService.getUsageDaily(orgId, FeatureId.DOMAINS) - const remoteExitNodes = await usageService.getUsageDaily(orgId, FeatureId.REMOTE_EXIT_NODES) - const egressData = await usageService.getUsage(orgId, FeatureId.EGRESS_DATA_MB) + const siteUptime = await usageService.getUsage(orgId, FeatureId.SITE_UPTIME); + const users = await usageService.getUsageDaily(orgId, FeatureId.USERS); + const domains = await usageService.getUsageDaily(orgId, FeatureId.DOMAINS); + const remoteExitNodes = await usageService.getUsageDaily(orgId, FeatureId.REMOTE_EXIT_NODES); + const egressData = await usageService.getUsage(orgId, FeatureId.EGRESS_DATA_MB); if (siteUptime) { usageData.push(siteUptime); diff --git a/server/routers/private/hybrid.ts b/server/routers/private/hybrid.ts index c8b0960d..dc55bc0d 100644 --- a/server/routers/private/hybrid.ts +++ b/server/routers/private/hybrid.ts @@ -53,7 +53,7 @@ import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; -import { getTraefikConfig } from "../../lib/traefik" +import { getTraefikConfig } from "../../lib/traefik"; import { generateGerbilConfig, generateRelayMappings, diff --git a/server/routers/ws/privateWs.ts b/server/routers/ws/privateWs.ts index 52bb94d8..d94ccf5e 100644 --- a/server/routers/ws/privateWs.ts +++ b/server/routers/ws/privateWs.ts @@ -198,7 +198,7 @@ const processPendingMessages = async ( `Processing ${ws.pendingMessages.length} pending messages for ${clientType.toUpperCase()} ID: ${clientId}` ); - let jobs = []; + const jobs = []; for (const messageData of ws.pendingMessages) { jobs.push(processMessage(ws, messageData, clientId, clientType)); } @@ -216,7 +216,7 @@ const NODE_ID = uuidv4(); const REDIS_CHANNEL = "websocket_messages"; // Client tracking map (local to this node) -let connectedClients: Map = new Map(); +const connectedClients: Map = new Map(); // Recovery tracking let isRedisRecoveryInProgress = false; diff --git a/src/components/PermissionsSelectBox.tsx b/src/components/PermissionsSelectBox.tsx index 048c21eb..38723613 100644 --- a/src/components/PermissionsSelectBox.tsx +++ b/src/components/PermissionsSelectBox.tsx @@ -149,7 +149,7 @@ function getActionsCategories(root: boolean) { if (build == "saas") { actionsByCategory["SAAS"] = { ["Send Usage Notification Email"]: "sendUsageNotification", - } + }; } } From 956869ab5844f58df077851cfef5cad93b6b2195 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 5 Oct 2025 15:40:54 -0700 Subject: [PATCH 167/322] add strip duplicate sesion middleware --- server/apiServer.ts | 2 + server/internalServer.ts | 2 + server/middlewares/stripDuplicateSessions.ts | 49 ++++++++++++++++++++ server/nextServer.ts | 3 ++ 4 files changed, 56 insertions(+) create mode 100644 server/middlewares/stripDuplicateSessions.ts diff --git a/server/apiServer.ts b/server/apiServer.ts index 7c64c14f..0b5a6305 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -21,6 +21,7 @@ import HttpCode from "./types/HttpCode"; import requestTimeoutMiddleware from "./middlewares/requestTimeout"; import { createStore } from "@server/lib/private/rateLimitStore"; import hybridRouter from "@server/routers/private/hybrid"; +import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; const dev = config.isDev; const externalPort = config.getRawConfig().server.external_port; @@ -73,6 +74,7 @@ export function createApiServer() { apiServer.use(csrfProtectionMiddleware); } + apiServer.use(stripDuplicateSesions); apiServer.use(cookieParser()); apiServer.use(express.json()); diff --git a/server/internalServer.ts b/server/internalServer.ts index 92da137f..0ba64eec 100644 --- a/server/internalServer.ts +++ b/server/internalServer.ts @@ -9,6 +9,7 @@ import { notFoundMiddleware } from "@server/middlewares"; import internal from "@server/routers/internal"; +import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; const internalPort = config.getRawConfig().server.internal_port; @@ -17,6 +18,7 @@ export function createInternalServer() { internalServer.use(helmet()); internalServer.use(cors()); + internalServer.use(stripDuplicateSesions); internalServer.use(cookieParser()); internalServer.use(express.json()); diff --git a/server/middlewares/stripDuplicateSessions.ts b/server/middlewares/stripDuplicateSessions.ts new file mode 100644 index 00000000..2558e511 --- /dev/null +++ b/server/middlewares/stripDuplicateSessions.ts @@ -0,0 +1,49 @@ +import { NextFunction, Response } from "express"; +import ErrorResponse from "@server/types/ErrorResponse"; +import { + SESSION_COOKIE_NAME, + validateSessionToken +} from "@server/auth/sessions/app"; + +export const stripDuplicateSesions = async ( + req: any, + res: Response, + next: NextFunction +) => { + const cookieHeader: string | undefined = req.headers.cookie; + if (!cookieHeader) { + return next(); + } + + const cookies = cookieHeader.split(";").map((cookie) => cookie.trim()); + const sessionCookies = cookies.filter((cookie) => + cookie.startsWith(`${SESSION_COOKIE_NAME}=`) + ); + + const validSessions: string[] = []; + if (sessionCookies.length > 1) { + for (const cookie of sessionCookies) { + const cookieValue = cookie.split("=")[1]; + const res = await validateSessionToken(cookieValue); + if (res.session && res.user) { + validSessions.push(cookieValue); + } + } + + if (validSessions.length > 0) { + const newCookieHeader = cookies.filter((cookie) => { + if (cookie.startsWith(`${SESSION_COOKIE_NAME}=`)) { + const cookieValue = cookie.split("=")[1]; + return validSessions.includes(cookieValue); + } + return true; + }); + req.headers.cookie = newCookieHeader.join("; "); + if (req.cookies) { + req.cookies[SESSION_COOKIE_NAME] = validSessions[0]; + } + } + } + + return next(); +}; diff --git a/server/nextServer.ts b/server/nextServer.ts index 4c96d04f..78169f03 100644 --- a/server/nextServer.ts +++ b/server/nextServer.ts @@ -3,6 +3,7 @@ import express from "express"; import { parse } from "url"; import logger from "@server/logger"; import config from "@server/lib/config"; +import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; const nextPort = config.getRawConfig().server.next_port; @@ -15,6 +16,8 @@ export async function createNextServer() { const nextServer = express(); + nextServer.use(stripDuplicateSesions); + nextServer.all("/{*splat}", (req, res) => { const parsedUrl = parse(req.url!, true); return handle(req, res, parsedUrl); From e4c0a157e3d5c1b83732fec2deeb032b9c0f3630 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 5 Oct 2025 15:46:46 -0700 Subject: [PATCH 168/322] Add to oss traefik config and fix create/update --- server/lib/traefik/getTraefikConfig.ts | 36 ++++++++++++++++++++++---- server/routers/target/createTarget.ts | 5 +++- server/routers/target/updateTarget.ts | 5 +++- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 598ce984..97a6826d 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -1,5 +1,5 @@ import { db, exitNodes, targetHealthCheck } from "@server/db"; -import { and, eq, inArray, or, isNull, ne, isNotNull } from "drizzle-orm"; +import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; import logger from "@server/logger"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; @@ -124,6 +124,8 @@ export async function getTraefikConfig( pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, rewritePathType: targets.rewritePathType, + priority: targets.priority, + // Site fields siteId: sites.siteId, siteType: sites.type, @@ -152,7 +154,8 @@ export async function getTraefikConfig( ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true : eq(resources.http, true) ) - ); + ) + .orderBy(desc(targets.priority), targets.targetId); // stable ordering // Group by resource and include targets with their unique site data const resourcesMap = new Map(); @@ -163,6 +166,7 @@ export async function getTraefikConfig( const pathMatchType = row.pathMatchType || ""; const rewritePath = row.rewritePath || ""; const rewritePathType = row.rewritePathType || ""; + const priority = row.priority ?? 100; // Create a unique key combining resourceId, path config, and rewrite config const pathKey = [targetPath, pathMatchType, rewritePath, rewritePathType] @@ -202,7 +206,8 @@ export async function getTraefikConfig( path: row.path, // the targets will all have the same path pathMatchType: row.pathMatchType, // the targets will all have the same pathMatchType rewritePath: row.rewritePath, - rewritePathType: row.rewritePathType + rewritePathType: row.rewritePathType, + priority: priority // may be null, we fallback later }); } @@ -217,6 +222,7 @@ export async function getTraefikConfig( enabled: row.targetEnabled, rewritePath: row.rewritePath, rewritePathType: row.rewritePathType, + priority: row.priority, site: { siteId: row.siteId, type: row.siteType, @@ -402,10 +408,30 @@ export async function getTraefikConfig( // Build routing rules let rule = `Host(\`${fullDomain}\`)`; - let priority = 100; + + // priority logic + let priority: number; + if (resource.priority && resource.priority != 100) { + priority = resource.priority; + } else { + priority = 100; + if (resource.path && resource.pathMatchType) { + priority += 10; + if (resource.pathMatchType === "exact") { + priority += 5; + } else if (resource.pathMatchType === "prefix") { + priority += 3; + } else if (resource.pathMatchType === "regex") { + priority += 2; + } + if (resource.path === "/") { + priority = 1; // lowest for catch-all + } + } + } if (resource.path && resource.pathMatchType) { - priority += 1; + // priority += 1; // add path to rule based on match type let path = resource.path; // if the path doesn't start with a /, add it diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index d5be025b..73e21521 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -211,7 +211,10 @@ export async function createTarget( internalPort, enabled: targetData.enabled, path: targetData.path, - pathMatchType: targetData.pathMatchType + pathMatchType: targetData.pathMatchType, + rewritePath: targetData.rewritePath, + rewritePathType: targetData.rewritePathType, + priority: targetData.priority }) .returning(); diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index d66c7cd0..d332609d 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -199,7 +199,10 @@ export async function updateTarget( internalPort, enabled: parsedBody.data.enabled, path: parsedBody.data.path, - pathMatchType: parsedBody.data.pathMatchType + pathMatchType: parsedBody.data.pathMatchType, + priority: parsedBody.data.priority, + rewritePath: parsedBody.data.rewritePath, + rewritePathType: parsedBody.data.rewritePathType }) .where(eq(targets.targetId, targetId)) .returning(); From b167d94eada4ee4369cec02eb4f0f5284a3bd745 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 5 Oct 2025 16:50:46 -0700 Subject: [PATCH 169/322] update cors to check array --- server/middlewares/private/corsWithLoginPage.ts | 7 +++++++ src/actions/server.ts | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/server/middlewares/private/corsWithLoginPage.ts b/server/middlewares/private/corsWithLoginPage.ts index 03725c50..95867fa1 100644 --- a/server/middlewares/private/corsWithLoginPage.ts +++ b/server/middlewares/private/corsWithLoginPage.ts @@ -78,6 +78,13 @@ export function corsWithLoginPageSupport(corsConfig: any) { return callback(null, true); } + if ( + corsConfig?.origins && + corsConfig.origins.includes(origin) + ) { + return callback(null, true); + } + // If origin doesn't match dashboard URL, check if it's a valid loginPage domain const isValidDomain = await isValidLoginPageDomain(originHost); diff --git a/src/actions/server.ts b/src/actions/server.ts index a6ccff8c..b9dc6e55 100644 --- a/src/actions/server.ts +++ b/src/actions/server.ts @@ -62,7 +62,6 @@ function parseSetCookieString( : new URL(env.app.dashboardUrl).hostname; if (d) { options.domain = d; - console.log("Setting cookie domain to:", d); } } From 9e9a81d9e87239f56457fdb84e4284ece73bb9a9 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:24 -0700 Subject: [PATCH 170/322] New translations en-us.json (Bulgarian) --- messages/bg-BG.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 29915cbe..9cc9e9f0 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "Двуфакторното удостоверяване е необходимо за регистрация на ключ за защита.", "twoFactor": "Двуфакторно удостоверяване", "adminEnabled2FaOnYourAccount": "Вашият администратор е активирал двуфакторно удостоверяване за {email}. Моля, завършете процеса по настройка, за да продължите.", - "continueToApplication": "Продължаване към приложението", + "continueToApplication": "Продължете към приложението", "securityKeyAdd": "Добавяне на ключ за сигурност", "securityKeyRegisterTitle": "Регистриране на нов ключ за сигурност", "securityKeyRegisterDescription": "Свържете ключа за сигурност и въведете име, по което да го идентифицирате", From 979860a951df2b8f3774f47c37d55adbf352e81f Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:25 -0700 Subject: [PATCH 171/322] New translations en-us.json (Czech) --- messages/cs-CZ.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index ab264302..f1106fb8 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "Pro registraci bezpečnostního klíče je nutné dvoufaktorové ověření.", "twoFactor": "Dvoufaktorové ověření", "adminEnabled2FaOnYourAccount": "Váš správce povolil dvoufaktorové ověřování pro {email}. Chcete-li pokračovat, dokončete proces nastavení.", - "continueToApplication": "Pokračovat do aplikace", + "continueToApplication": "Pokračovat v aplikaci", "securityKeyAdd": "Přidat bezpečnostní klíč", "securityKeyRegisterTitle": "Registrovat nový bezpečnostní klíč", "securityKeyRegisterDescription": "Připojte svůj bezpečnostní klíč a zadejte jméno pro jeho identifikaci", From a95f2e76f4cfdae9097071e95c24a52e0ea8b3db Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:27 -0700 Subject: [PATCH 172/322] New translations en-us.json (Italian) --- messages/it-IT.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/it-IT.json b/messages/it-IT.json index 40f9811f..ca22ba63 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "È richiesta l'autenticazione a due fattori per registrare una chiave di sicurezza.", "twoFactor": "Autenticazione a Due Fattori", "adminEnabled2FaOnYourAccount": "Il tuo amministratore ha abilitato l'autenticazione a due fattori per {email}. Completa il processo di configurazione per continuare.", - "continueToApplication": "Continua all'Applicazione", + "continueToApplication": "Continua con l'applicazione", "securityKeyAdd": "Aggiungi Chiave di Sicurezza", "securityKeyRegisterTitle": "Registra Nuova Chiave di Sicurezza", "securityKeyRegisterDescription": "Collega la tua chiave di sicurezza e inserisci un nome per identificarla", From 7b33dc591d858c8eb1154deebbb59cbf5d87376e Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:29 -0700 Subject: [PATCH 173/322] New translations en-us.json (Dutch) --- messages/nl-NL.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/nl-NL.json b/messages/nl-NL.json index fb370345..fb82fbb6 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "Tweestapsverificatie is vereist om een beveiligingssleutel te registreren.", "twoFactor": "Tweestapsverificatie", "adminEnabled2FaOnYourAccount": "Je beheerder heeft tweestapsverificatie voor {email} ingeschakeld. Voltooi het instellingsproces om verder te gaan.", - "continueToApplication": "Doorgaan naar de applicatie", + "continueToApplication": "Doorgaan naar applicatie", "securityKeyAdd": "Beveiligingssleutel toevoegen", "securityKeyRegisterTitle": "Nieuwe beveiligingssleutel registreren", "securityKeyRegisterDescription": "Verbind je beveiligingssleutel en voer een naam in om deze te identificeren", From c2c29e2cd2b36e23f7a782133ef7bb963107406b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:31 -0700 Subject: [PATCH 174/322] New translations en-us.json (Portuguese) --- messages/pt-PT.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/pt-PT.json b/messages/pt-PT.json index ff0c093d..1d61c581 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "A autenticação de dois fatores é necessária para registrar uma chave de segurança.", "twoFactor": "Autenticação de Dois Fatores", "adminEnabled2FaOnYourAccount": "Seu administrador ativou a autenticação de dois fatores para {email}. Complete o processo de configuração para continuar.", - "continueToApplication": "Continuar para Aplicativo", + "continueToApplication": "Continuar para o aplicativo", "securityKeyAdd": "Adicionar Chave de Segurança", "securityKeyRegisterTitle": "Registrar Nova Chave de Segurança", "securityKeyRegisterDescription": "Conecte sua chave de segurança e insira um nome para identificá-la", From 19e15f4ef5ed1e213d0a003b25696cfcce11ce8b Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 5 Oct 2025 17:16:34 -0700 Subject: [PATCH 175/322] New translations en-us.json (Chinese Simplified) --- messages/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 94ccd9ae..4f1d9c14 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -1333,7 +1333,7 @@ "twoFactorRequired": "注册安全密钥需要两步验证。", "twoFactor": "两步验证", "adminEnabled2FaOnYourAccount": "管理员已为{email}启用两步验证。请完成设置以继续。", - "continueToApplication": "继续到应用程序", + "continueToApplication": "继续应用", "securityKeyAdd": "添加安全密钥", "securityKeyRegisterTitle": "注册新安全密钥", "securityKeyRegisterDescription": "连接您的安全密钥并输入名称以便识别", From b14ddc07fb604a0610c7e8c2b9623146cc8e3718 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 01:24:59 +0000 Subject: [PATCH 176/322] Bump @types/node from 24.6.1 to 24.6.2 in the dev-patch-updates group Bumps the dev-patch-updates group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@types/node` from 24.6.1 to 24.6.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 24.6.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e81796e3..ee8e4e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.1", + "@types/node": "24.6.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", @@ -8633,9 +8633,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", - "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9be26963..ba89d422 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.1", + "@types/node": "24.6.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", From 389834f73562d736166295a0c8a167dce79f5662 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 01:26:52 +0000 Subject: [PATCH 177/322] Bump @react-email/preview-server in the dev-minor-updates group Bumps the dev-minor-updates group with 1 update: [@react-email/preview-server](https://github.com/resend/react-email/tree/HEAD/packages/preview-server). Updates `@react-email/preview-server` from 4.1.0 to 4.2.12 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/preview-server/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/preview-server@4.2.12/packages/preview-server) --- updated-dependencies: - dependency-name: "@react-email/preview-server" dependency-version: 4.2.12 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 1211 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 1195 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index e81796e3..7af8809b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.1.0", + "@react-email/preview-server": "4.2.12", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", @@ -3485,6 +3485,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -6308,15 +6309,15 @@ } }, "node_modules/@react-email/preview-server": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.1.0.tgz", - "integrity": "sha512-wz4dQyQtIjAavJ0bVIu+fkZMUUmfYkKGYAwFFD6YvNdNwhU+HdhxfzdCJ1zwOjkc4ETCGtA3rJIKQ41D3La3jw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.2.12.tgz", + "integrity": "sha512-FVM3h6vJQdjk5E3P8ts8zAPeZSyefcIDWGmy/Tnwl9zVlwiGcQSjGVPmAHlw1DdJnkK79OeC/VYisMlq0w6W8Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "7.26.10", - "@babel/parser": "^7.27.0", - "@babel/traverse": "^7.27.0", + "@babel/parser": "7.27.0", + "@babel/traverse": "7.27.0", "@lottiefiles/dotlottie-react": "0.13.3", "@radix-ui/colors": "3.0.0", "@radix-ui/react-collapsible": "1.1.7", @@ -6332,16 +6333,15 @@ "@types/react-dom": "19.0.4", "@types/webpack": "5.28.5", "autoprefixer": "10.4.21", - "chalk": "^4.1.2", "clsx": "2.1.1", - "esbuild": "^0.25.0", - "framer-motion": "12.7.5", + "esbuild": "0.25.0", + "framer-motion": "12.23.12", "json5": "2.2.3", - "log-symbols": "^4.1.0", + "log-symbols": "4.1.0", "module-punycode": "npm:punycode@2.3.1", - "next": "^15.3.2", + "next": "15.5.2", "node-html-parser": "7.0.1", - "ora": "^5.4.1", + "ora": "5.4.1", "pretty-bytes": "6.1.1", "prism-react-renderer": "2.4.1", "react": "19.0.0", @@ -6358,6 +6358,1006 @@ "zod": "3.24.3" } }, + "node_modules/@react-email/preview-server/node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/env": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", + "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-arm64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", + "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-x64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", + "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", + "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", + "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", + "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", + "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", + "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", + "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@react-email/preview-server/node_modules/@radix-ui/primitive": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", @@ -6942,6 +7942,57 @@ "node": ">= 6" } }, + "node_modules/@react-email/preview-server/node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@react-email/preview-server/node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -6952,6 +8003,103 @@ "jiti": "bin/jiti.js" } }, + "node_modules/@react-email/preview-server/node_modules/next": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", + "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "15.5.2", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.2", + "@next/swc-darwin-x64": "15.5.2", + "@next/swc-linux-arm64-gnu": "15.5.2", + "@next/swc-linux-arm64-musl": "15.5.2", + "@next/swc-linux-x64-gnu": "15.5.2", + "@next/swc-linux-x64-musl": "15.5.2", + "@next/swc-win32-arm64-msvc": "15.5.2", + "@next/swc-win32-x64-msvc": "15.5.2", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/next/node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, "node_modules/@react-email/preview-server/node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -6965,6 +8113,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@react-email/preview-server/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/@react-email/preview-server/node_modules/postcss-load-config": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", @@ -12800,14 +13977,14 @@ } }, "node_modules/framer-motion": { - "version": "12.7.5", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.5.tgz", - "integrity": "sha512-iD+vBOLn8E8bwBAFUQ1DYXjivm+cGGPgQUQ4Doleq7YP/zHdozUVwAMBJwOOfCTbtM8uOooMi77noD261Kxiyw==", + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", + "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==", "dev": true, "license": "MIT", "dependencies": { - "motion-dom": "^12.7.5", - "motion-utils": "^12.7.5", + "motion-dom": "^12.23.12", + "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { diff --git a/package.json b/package.json index 9be26963..bd146975 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.1.0", + "@react-email/preview-server": "4.2.12", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", From 9e8e00d4bbf20f2638c823d2847c48bd705d6e46 Mon Sep 17 00:00:00 2001 From: OddMagnet Date: Thu, 25 Sep 2025 14:16:02 +0200 Subject: [PATCH 178/322] Update traefik dynamic config to also use resource name --- server/lib/traefik/privateGetTraefikConfig.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts index 7f1ff614..d787b607 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -54,6 +54,7 @@ export async function getTraefikConfig( .select({ // Resource fields resourceId: resources.resourceId, + resourceName: resources.name, fullDomain: resources.fullDomain, ssl: resources.ssl, http: resources.http, @@ -125,7 +126,8 @@ export async function getTraefikConfig( resourcesWithTargetsAndSites.forEach((row) => { const resourceId = row.resourceId; - const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths + const resourceName = sanitize(row.resourceName) || ""; + const targetPath = sanitize(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; if (filterOutNamespaceDomains && row.domainNamespaceId) { @@ -139,6 +141,7 @@ export async function getTraefikConfig( if (!resourcesMap.has(mapKey)) { resourcesMap.set(mapKey, { resourceId: row.resourceId, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -206,8 +209,8 @@ export async function getTraefikConfig( for (const [key, resource] of resourcesMap.entries()) { const targets = resource.targets; - const routerName = `${key}-router`; - const serviceName = `${key}-service`; + const routerName = `${key}-${resource.name}-router`; + const serviceName = `${key}-${resource.name}-service`; const fullDomain = `${resource.fullDomain}`; const transportName = `${key}-transport`; const headersMiddlewareName = `${key}-headers-middleware`; @@ -681,12 +684,12 @@ export async function getTraefikConfig( return config_output; } -function sanitizePath(path: string | null | undefined): string | undefined { - if (!path) return undefined; - // clean any non alphanumeric characters from the path and replace with dashes - // the path cant be too long either, so limit to 50 characters - if (path.length > 50) { - path = path.substring(0, 50); +function sanitize(input: string | null | undefined): string | undefined { + if (!input) return undefined; + // clean any non alphanumeric characters from the input and replace with dashes + // the input cant be too long either, so limit to 50 characters + if (input.length > 50) { + input = input.substring(0, 50); } - return path.replace(/[^a-zA-Z0-9]/g, ""); + return input.replace(/[^a-zA-Z0-9]/g, ""); } From cd285cc019e233f52f90ccb7a0669ccb4e1b0eed Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 09:50:18 -0700 Subject: [PATCH 179/322] Clean up and copy to getTraefikConfig --- server/lib/traefik/getTraefikConfig.ts | 113 +++--------------- server/lib/traefik/privateGetTraefikConfig.ts | 21 +--- server/lib/traefik/utils.ts | 81 +++++++++++++ 3 files changed, 101 insertions(+), 114 deletions(-) create mode 100644 server/lib/traefik/utils.ts diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 598ce984..3a5e2b3e 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -5,77 +5,11 @@ import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; import createPathRewriteMiddleware from "./middleware"; +import { sanitize, validatePathRewriteConfig } from "./utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const badgerMiddlewareName = "badger"; - -function validatePathRewriteConfig( - path: string | null, - pathMatchType: string | null, - rewritePath: string | null, - rewritePathType: string | null -): { isValid: boolean; error?: string } { - // If no path matching is configured, no rewriting is possible - if (!path || !pathMatchType) { - if (rewritePath || rewritePathType) { - return { - isValid: false, - error: "Path rewriting requires path matching to be configured" - }; - } - return { isValid: true }; - } - - if (rewritePathType !== "stripPrefix") { - if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { - return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; - } - } - - - if (!rewritePath || !rewritePathType) { - return { isValid: true }; - } - - const validPathMatchTypes = ["exact", "prefix", "regex"]; - if (!validPathMatchTypes.includes(pathMatchType)) { - return { - isValid: false, - error: `Invalid pathMatchType: ${pathMatchType}. Must be one of: ${validPathMatchTypes.join(", ")}` - }; - } - - const validRewritePathTypes = ["exact", "prefix", "regex", "stripPrefix"]; - if (!validRewritePathTypes.includes(rewritePathType)) { - return { - isValid: false, - error: `Invalid rewritePathType: ${rewritePathType}. Must be one of: ${validRewritePathTypes.join(", ")}` - }; - } - - if (pathMatchType === "regex") { - try { - new RegExp(path); - } catch (e) { - return { - isValid: false, - error: `Invalid regex pattern in path: ${path}` - }; - } - } - - - // Additional validation for stripPrefix - if (rewritePathType === "stripPrefix") { - if (pathMatchType !== "prefix") { - logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); - } - } - - return { isValid: true }; -} - export async function getTraefikConfig( exitNodeId: number, siteTypes: string[], @@ -99,6 +33,7 @@ export async function getTraefikConfig( .select({ // Resource fields resourceId: resources.resourceId, + resourceName: resources.name, fullDomain: resources.fullDomain, ssl: resources.ssl, http: resources.http, @@ -159,7 +94,8 @@ export async function getTraefikConfig( resourcesWithTargetsAndSites.forEach((row) => { const resourceId = row.resourceId; - const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths + const resourceName = sanitize(row.resourceName) || ""; + const targetPath = sanitize(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; const rewritePath = row.rewritePath || ""; const rewritePathType = row.rewritePathType || ""; @@ -169,8 +105,9 @@ export async function getTraefikConfig( .filter(Boolean) .join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); + const key = sanitize(mapKey); - if (!resourcesMap.has(mapKey)) { + if (!resourcesMap.has(key)) { const validation = validatePathRewriteConfig( row.path, row.pathMatchType, @@ -183,8 +120,9 @@ export async function getTraefikConfig( return; } - resourcesMap.set(mapKey, { + resourcesMap.set(key, { resourceId: row.resourceId, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -207,7 +145,7 @@ export async function getTraefikConfig( } // Add target with its associated site data - resourcesMap.get(mapKey).targets.push({ + resourcesMap.get(key).targets.push({ resourceId: row.resourceId, targetId: row.targetId, ip: row.ip, @@ -248,13 +186,11 @@ export async function getTraefikConfig( for (const [key, resource] of resourcesMap.entries()) { const targets = resource.targets; - const sanatizedKey = sanitizeForMiddlewareName(key); - - const routerName = `${sanatizedKey}-router`; - const serviceName = `${sanatizedKey}-service`; + const routerName = `${key}-${resource.name}-router`; + const serviceName = `${key}-${resource.name}-service`; const fullDomain = `${resource.fullDomain}`; - const transportName = `${sanatizedKey}-transport`; - const headersMiddlewareName = `${sanatizedKey}-headers-middleware`; + const transportName = `${key}-transport`; + const headersMiddlewareName = `${key}-headers-middleware`; if (!resource.enabled) { continue; @@ -328,7 +264,7 @@ export async function getTraefikConfig( resource.rewritePathType) { // Create a unique middleware name - const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${sanitizeForMiddlewareName(key)}`; + const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${key}`; try { const rewriteResult = createPathRewriteMiddleware( @@ -642,24 +578,3 @@ export async function getTraefikConfig( } return config_output; } - -function sanitizePath(path: string | null | undefined): string | undefined { - if (!path) return undefined; - - const trimmed = path.trim(); - if (!trimmed) return undefined; - - // Preserve path structure for rewriting, only warn if very long - if (trimmed.length > 1000) { - logger.warn(`Path exceeds 1000 characters: ${trimmed.substring(0, 100)}...`); - return trimmed.substring(0, 1000); - } - - return trimmed; -} - -function sanitizeForMiddlewareName(str: string): string { - // Replace any characters that aren't alphanumeric or dash with dash - // and remove consecutive dashes - return str.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); -} \ No newline at end of file diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts index d787b607..67d174d5 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -11,7 +11,6 @@ * This file is not licensed under the AGPLv3. */ -import { Request, Response } from "express"; import { certificates, db, @@ -26,6 +25,7 @@ import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; +import { sanitize } from "./utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; @@ -137,9 +137,10 @@ export async function getTraefikConfig( // Create a unique key combining resourceId and path+pathMatchType const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); + const key = sanitize(mapKey); - if (!resourcesMap.has(mapKey)) { - resourcesMap.set(mapKey, { + if (!resourcesMap.has(key)) { + resourcesMap.set(key, { resourceId: row.resourceId, name: resourceName, fullDomain: row.fullDomain, @@ -163,7 +164,7 @@ export async function getTraefikConfig( } // Add target with its associated site data - resourcesMap.get(mapKey).targets.push({ + resourcesMap.get(key).targets.push({ resourceId: row.resourceId, targetId: row.targetId, ip: row.ip, @@ -682,14 +683,4 @@ export async function getTraefikConfig( } return config_output; -} - -function sanitize(input: string | null | undefined): string | undefined { - if (!input) return undefined; - // clean any non alphanumeric characters from the input and replace with dashes - // the input cant be too long either, so limit to 50 characters - if (input.length > 50) { - input = input.substring(0, 50); - } - return input.replace(/[^a-zA-Z0-9]/g, ""); -} +} \ No newline at end of file diff --git a/server/lib/traefik/utils.ts b/server/lib/traefik/utils.ts new file mode 100644 index 00000000..37ebfa0b --- /dev/null +++ b/server/lib/traefik/utils.ts @@ -0,0 +1,81 @@ +import logger from "@server/logger"; + +export function sanitize(input: string | null | undefined): string | undefined { + if (!input) return undefined; + // clean any non alphanumeric characters from the input and replace with dashes + // the input cant be too long either, so limit to 50 characters + if (input.length > 50) { + input = input.substring(0, 50); + } + return input + .replace(/[^a-zA-Z0-9-]/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); +} + +export function validatePathRewriteConfig( + path: string | null, + pathMatchType: string | null, + rewritePath: string | null, + rewritePathType: string | null +): { isValid: boolean; error?: string } { + // If no path matching is configured, no rewriting is possible + if (!path || !pathMatchType) { + if (rewritePath || rewritePathType) { + return { + isValid: false, + error: "Path rewriting requires path matching to be configured" + }; + } + return { isValid: true }; + } + + if (rewritePathType !== "stripPrefix") { + if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { + return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; + } + } + + + if (!rewritePath || !rewritePathType) { + return { isValid: true }; + } + + const validPathMatchTypes = ["exact", "prefix", "regex"]; + if (!validPathMatchTypes.includes(pathMatchType)) { + return { + isValid: false, + error: `Invalid pathMatchType: ${pathMatchType}. Must be one of: ${validPathMatchTypes.join(", ")}` + }; + } + + const validRewritePathTypes = ["exact", "prefix", "regex", "stripPrefix"]; + if (!validRewritePathTypes.includes(rewritePathType)) { + return { + isValid: false, + error: `Invalid rewritePathType: ${rewritePathType}. Must be one of: ${validRewritePathTypes.join(", ")}` + }; + } + + if (pathMatchType === "regex") { + try { + new RegExp(path); + } catch (e) { + return { + isValid: false, + error: `Invalid regex pattern in path: ${path}` + }; + } + } + + + // Additional validation for stripPrefix + if (rewritePathType === "stripPrefix") { + if (pathMatchType !== "prefix") { + logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); + } + } + + return { isValid: true }; +} + From a8fce47ba039ab41251c81c1fa541ddb43a390db Mon Sep 17 00:00:00 2001 From: OddMagnet Date: Thu, 25 Sep 2025 14:16:02 +0200 Subject: [PATCH 180/322] Update traefik dynamic config to also use resource name --- server/lib/traefik/privateGetTraefikConfig.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts index f8e7b8b5..14f05382 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -54,6 +54,7 @@ export async function getTraefikConfig( .select({ // Resource fields resourceId: resources.resourceId, + resourceName: resources.name, fullDomain: resources.fullDomain, ssl: resources.ssl, http: resources.http, @@ -127,7 +128,8 @@ export async function getTraefikConfig( resourcesWithTargetsAndSites.forEach((row) => { const resourceId = row.resourceId; - const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths + const resourceName = sanitize(row.resourceName) || ""; + const targetPath = sanitize(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; const priority = row.priority ?? 100; @@ -142,6 +144,7 @@ export async function getTraefikConfig( if (!resourcesMap.has(mapKey)) { resourcesMap.set(mapKey, { resourceId: row.resourceId, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -211,8 +214,8 @@ export async function getTraefikConfig( for (const [key, resource] of resourcesMap.entries()) { const targets = resource.targets; - const routerName = `${key}-router`; - const serviceName = `${key}-service`; + const routerName = `${key}-${resource.name}-router`; + const serviceName = `${key}-${resource.name}-service`; const fullDomain = `${resource.fullDomain}`; const transportName = `${key}-transport`; const headersMiddlewareName = `${key}-headers-middleware`; @@ -707,12 +710,12 @@ export async function getTraefikConfig( return config_output; } -function sanitizePath(path: string | null | undefined): string | undefined { - if (!path) return undefined; - // clean any non alphanumeric characters from the path and replace with dashes - // the path cant be too long either, so limit to 50 characters - if (path.length > 50) { - path = path.substring(0, 50); +function sanitize(input: string | null | undefined): string | undefined { + if (!input) return undefined; + // clean any non alphanumeric characters from the input and replace with dashes + // the input cant be too long either, so limit to 50 characters + if (input.length > 50) { + input = input.substring(0, 50); } - return path.replace(/[^a-zA-Z0-9]/g, ""); + return input.replace(/[^a-zA-Z0-9]/g, ""); } From 4c412528f5389b96f38b5da0aa61c2ae66c4c277 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 09:50:18 -0700 Subject: [PATCH 181/322] Clean up and copy to getTraefikConfig --- server/lib/traefik/getTraefikConfig.ts | 113 +++--------------- server/lib/traefik/privateGetTraefikConfig.ts | 21 +--- server/lib/traefik/utils.ts | 81 +++++++++++++ 3 files changed, 101 insertions(+), 114 deletions(-) create mode 100644 server/lib/traefik/utils.ts diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 97a6826d..7e1ce562 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -5,77 +5,11 @@ import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; import createPathRewriteMiddleware from "./middleware"; +import { sanitize, validatePathRewriteConfig } from "./utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const badgerMiddlewareName = "badger"; - -function validatePathRewriteConfig( - path: string | null, - pathMatchType: string | null, - rewritePath: string | null, - rewritePathType: string | null -): { isValid: boolean; error?: string } { - // If no path matching is configured, no rewriting is possible - if (!path || !pathMatchType) { - if (rewritePath || rewritePathType) { - return { - isValid: false, - error: "Path rewriting requires path matching to be configured" - }; - } - return { isValid: true }; - } - - if (rewritePathType !== "stripPrefix") { - if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { - return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; - } - } - - - if (!rewritePath || !rewritePathType) { - return { isValid: true }; - } - - const validPathMatchTypes = ["exact", "prefix", "regex"]; - if (!validPathMatchTypes.includes(pathMatchType)) { - return { - isValid: false, - error: `Invalid pathMatchType: ${pathMatchType}. Must be one of: ${validPathMatchTypes.join(", ")}` - }; - } - - const validRewritePathTypes = ["exact", "prefix", "regex", "stripPrefix"]; - if (!validRewritePathTypes.includes(rewritePathType)) { - return { - isValid: false, - error: `Invalid rewritePathType: ${rewritePathType}. Must be one of: ${validRewritePathTypes.join(", ")}` - }; - } - - if (pathMatchType === "regex") { - try { - new RegExp(path); - } catch (e) { - return { - isValid: false, - error: `Invalid regex pattern in path: ${path}` - }; - } - } - - - // Additional validation for stripPrefix - if (rewritePathType === "stripPrefix") { - if (pathMatchType !== "prefix") { - logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); - } - } - - return { isValid: true }; -} - export async function getTraefikConfig( exitNodeId: number, siteTypes: string[], @@ -99,6 +33,7 @@ export async function getTraefikConfig( .select({ // Resource fields resourceId: resources.resourceId, + resourceName: resources.name, fullDomain: resources.fullDomain, ssl: resources.ssl, http: resources.http, @@ -162,7 +97,8 @@ export async function getTraefikConfig( resourcesWithTargetsAndSites.forEach((row) => { const resourceId = row.resourceId; - const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths + const resourceName = sanitize(row.resourceName) || ""; + const targetPath = sanitize(row.path) || ""; // Handle null/undefined paths const pathMatchType = row.pathMatchType || ""; const rewritePath = row.rewritePath || ""; const rewritePathType = row.rewritePathType || ""; @@ -173,8 +109,9 @@ export async function getTraefikConfig( .filter(Boolean) .join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); + const key = sanitize(mapKey); - if (!resourcesMap.has(mapKey)) { + if (!resourcesMap.has(key)) { const validation = validatePathRewriteConfig( row.path, row.pathMatchType, @@ -187,8 +124,9 @@ export async function getTraefikConfig( return; } - resourcesMap.set(mapKey, { + resourcesMap.set(key, { resourceId: row.resourceId, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -212,7 +150,7 @@ export async function getTraefikConfig( } // Add target with its associated site data - resourcesMap.get(mapKey).targets.push({ + resourcesMap.get(key).targets.push({ resourceId: row.resourceId, targetId: row.targetId, ip: row.ip, @@ -254,13 +192,11 @@ export async function getTraefikConfig( for (const [key, resource] of resourcesMap.entries()) { const targets = resource.targets; - const sanatizedKey = sanitizeForMiddlewareName(key); - - const routerName = `${sanatizedKey}-router`; - const serviceName = `${sanatizedKey}-service`; + const routerName = `${key}-${resource.name}-router`; + const serviceName = `${key}-${resource.name}-service`; const fullDomain = `${resource.fullDomain}`; - const transportName = `${sanatizedKey}-transport`; - const headersMiddlewareName = `${sanatizedKey}-headers-middleware`; + const transportName = `${key}-transport`; + const headersMiddlewareName = `${key}-headers-middleware`; if (!resource.enabled) { continue; @@ -334,7 +270,7 @@ export async function getTraefikConfig( resource.rewritePathType) { // Create a unique middleware name - const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${sanitizeForMiddlewareName(key)}`; + const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${key}`; try { const rewriteResult = createPathRewriteMiddleware( @@ -668,24 +604,3 @@ export async function getTraefikConfig( } return config_output; } - -function sanitizePath(path: string | null | undefined): string | undefined { - if (!path) return undefined; - - const trimmed = path.trim(); - if (!trimmed) return undefined; - - // Preserve path structure for rewriting, only warn if very long - if (trimmed.length > 1000) { - logger.warn(`Path exceeds 1000 characters: ${trimmed.substring(0, 100)}...`); - return trimmed.substring(0, 1000); - } - - return trimmed; -} - -function sanitizeForMiddlewareName(str: string): string { - // Replace any characters that aren't alphanumeric or dash with dash - // and remove consecutive dashes - return str.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); -} \ No newline at end of file diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/lib/traefik/privateGetTraefikConfig.ts index 14f05382..1350e8b7 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/lib/traefik/privateGetTraefikConfig.ts @@ -11,7 +11,6 @@ * This file is not licensed under the AGPLv3. */ -import { Request, Response } from "express"; import { certificates, db, @@ -26,6 +25,7 @@ import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; +import { sanitize } from "./utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; @@ -140,9 +140,10 @@ export async function getTraefikConfig( // Create a unique key combining resourceId and path+pathMatchType const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); + const key = sanitize(mapKey); - if (!resourcesMap.has(mapKey)) { - resourcesMap.set(mapKey, { + if (!resourcesMap.has(key)) { + resourcesMap.set(key, { resourceId: row.resourceId, name: resourceName, fullDomain: row.fullDomain, @@ -167,7 +168,7 @@ export async function getTraefikConfig( } // Add target with its associated site data - resourcesMap.get(mapKey).targets.push({ + resourcesMap.get(key).targets.push({ resourceId: row.resourceId, targetId: row.targetId, ip: row.ip, @@ -708,14 +709,4 @@ export async function getTraefikConfig( } return config_output; -} - -function sanitize(input: string | null | undefined): string | undefined { - if (!input) return undefined; - // clean any non alphanumeric characters from the input and replace with dashes - // the input cant be too long either, so limit to 50 characters - if (input.length > 50) { - input = input.substring(0, 50); - } - return input.replace(/[^a-zA-Z0-9]/g, ""); -} +} \ No newline at end of file diff --git a/server/lib/traefik/utils.ts b/server/lib/traefik/utils.ts new file mode 100644 index 00000000..37ebfa0b --- /dev/null +++ b/server/lib/traefik/utils.ts @@ -0,0 +1,81 @@ +import logger from "@server/logger"; + +export function sanitize(input: string | null | undefined): string | undefined { + if (!input) return undefined; + // clean any non alphanumeric characters from the input and replace with dashes + // the input cant be too long either, so limit to 50 characters + if (input.length > 50) { + input = input.substring(0, 50); + } + return input + .replace(/[^a-zA-Z0-9-]/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); +} + +export function validatePathRewriteConfig( + path: string | null, + pathMatchType: string | null, + rewritePath: string | null, + rewritePathType: string | null +): { isValid: boolean; error?: string } { + // If no path matching is configured, no rewriting is possible + if (!path || !pathMatchType) { + if (rewritePath || rewritePathType) { + return { + isValid: false, + error: "Path rewriting requires path matching to be configured" + }; + } + return { isValid: true }; + } + + if (rewritePathType !== "stripPrefix") { + if ((rewritePath && !rewritePathType) || (!rewritePath && rewritePathType)) { + return { isValid: false, error: "Both rewritePath and rewritePathType must be specified together" }; + } + } + + + if (!rewritePath || !rewritePathType) { + return { isValid: true }; + } + + const validPathMatchTypes = ["exact", "prefix", "regex"]; + if (!validPathMatchTypes.includes(pathMatchType)) { + return { + isValid: false, + error: `Invalid pathMatchType: ${pathMatchType}. Must be one of: ${validPathMatchTypes.join(", ")}` + }; + } + + const validRewritePathTypes = ["exact", "prefix", "regex", "stripPrefix"]; + if (!validRewritePathTypes.includes(rewritePathType)) { + return { + isValid: false, + error: `Invalid rewritePathType: ${rewritePathType}. Must be one of: ${validRewritePathTypes.join(", ")}` + }; + } + + if (pathMatchType === "regex") { + try { + new RegExp(path); + } catch (e) { + return { + isValid: false, + error: `Invalid regex pattern in path: ${path}` + }; + } + } + + + // Additional validation for stripPrefix + if (rewritePathType === "stripPrefix") { + if (pathMatchType !== "prefix") { + logger.warn(`stripPrefix rewrite type is most effective with prefix path matching. Current match type: ${pathMatchType}`); + } + } + + return { isValid: true }; +} + From 1333e215535d072d43716f691f4be9c480fb14ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 01:26:52 +0000 Subject: [PATCH 182/322] Bump @react-email/preview-server in the dev-minor-updates group Bumps the dev-minor-updates group with 1 update: [@react-email/preview-server](https://github.com/resend/react-email/tree/HEAD/packages/preview-server). Updates `@react-email/preview-server` from 4.1.0 to 4.2.12 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/preview-server/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/preview-server@4.2.12/packages/preview-server) --- updated-dependencies: - dependency-name: "@react-email/preview-server" dependency-version: 4.2.12 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 1211 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 1195 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index e81796e3..7af8809b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.1.0", + "@react-email/preview-server": "4.2.12", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", @@ -3485,6 +3485,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -6308,15 +6309,15 @@ } }, "node_modules/@react-email/preview-server": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.1.0.tgz", - "integrity": "sha512-wz4dQyQtIjAavJ0bVIu+fkZMUUmfYkKGYAwFFD6YvNdNwhU+HdhxfzdCJ1zwOjkc4ETCGtA3rJIKQ41D3La3jw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.2.12.tgz", + "integrity": "sha512-FVM3h6vJQdjk5E3P8ts8zAPeZSyefcIDWGmy/Tnwl9zVlwiGcQSjGVPmAHlw1DdJnkK79OeC/VYisMlq0w6W8Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "7.26.10", - "@babel/parser": "^7.27.0", - "@babel/traverse": "^7.27.0", + "@babel/parser": "7.27.0", + "@babel/traverse": "7.27.0", "@lottiefiles/dotlottie-react": "0.13.3", "@radix-ui/colors": "3.0.0", "@radix-ui/react-collapsible": "1.1.7", @@ -6332,16 +6333,15 @@ "@types/react-dom": "19.0.4", "@types/webpack": "5.28.5", "autoprefixer": "10.4.21", - "chalk": "^4.1.2", "clsx": "2.1.1", - "esbuild": "^0.25.0", - "framer-motion": "12.7.5", + "esbuild": "0.25.0", + "framer-motion": "12.23.12", "json5": "2.2.3", - "log-symbols": "^4.1.0", + "log-symbols": "4.1.0", "module-punycode": "npm:punycode@2.3.1", - "next": "^15.3.2", + "next": "15.5.2", "node-html-parser": "7.0.1", - "ora": "^5.4.1", + "ora": "5.4.1", "pretty-bytes": "6.1.1", "prism-react-renderer": "2.4.1", "react": "19.0.0", @@ -6358,6 +6358,1006 @@ "zod": "3.24.3" } }, + "node_modules/@react-email/preview-server/node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/env": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", + "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-arm64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", + "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-x64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", + "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", + "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", + "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", + "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", + "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", + "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", + "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@react-email/preview-server/node_modules/@radix-ui/primitive": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", @@ -6942,6 +7942,57 @@ "node": ">= 6" } }, + "node_modules/@react-email/preview-server/node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@react-email/preview-server/node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -6952,6 +8003,103 @@ "jiti": "bin/jiti.js" } }, + "node_modules/@react-email/preview-server/node_modules/next": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", + "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "15.5.2", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.2", + "@next/swc-darwin-x64": "15.5.2", + "@next/swc-linux-arm64-gnu": "15.5.2", + "@next/swc-linux-arm64-musl": "15.5.2", + "@next/swc-linux-x64-gnu": "15.5.2", + "@next/swc-linux-x64-musl": "15.5.2", + "@next/swc-win32-arm64-msvc": "15.5.2", + "@next/swc-win32-x64-msvc": "15.5.2", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/next/node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, "node_modules/@react-email/preview-server/node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -6965,6 +8113,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@react-email/preview-server/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/@react-email/preview-server/node_modules/postcss-load-config": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", @@ -12800,14 +13977,14 @@ } }, "node_modules/framer-motion": { - "version": "12.7.5", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.5.tgz", - "integrity": "sha512-iD+vBOLn8E8bwBAFUQ1DYXjivm+cGGPgQUQ4Doleq7YP/zHdozUVwAMBJwOOfCTbtM8uOooMi77noD261Kxiyw==", + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", + "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==", "dev": true, "license": "MIT", "dependencies": { - "motion-dom": "^12.7.5", - "motion-utils": "^12.7.5", + "motion-dom": "^12.23.12", + "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { diff --git a/package.json b/package.json index 9be26963..bd146975 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.1.0", + "@react-email/preview-server": "4.2.12", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", From 2d30b155f24dde4ea8c77f7778b7a965ff724deb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 01:24:59 +0000 Subject: [PATCH 183/322] Bump @types/node from 24.6.1 to 24.6.2 in the dev-patch-updates group Bumps the dev-patch-updates group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@types/node` from 24.6.1 to 24.6.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 24.6.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7af8809b..4091a9f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.1", + "@types/node": "24.6.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", @@ -9810,9 +9810,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", - "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index bd146975..a5033c4c 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.1", + "@types/node": "24.6.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", From 850e9a734ad243ac968a0109d34a3ee89f09770f Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 10:14:02 -0700 Subject: [PATCH 184/322] Adding HTTP Header Authentication --- server/auth/actions.ts | 2 +- server/db/pg/schema.ts | 9 + server/db/queries/verifySessionQueries.ts | 10 +- server/db/sqlite/schema.ts | 11 ++ server/routers/badger/verifySession.ts | 104 ++++++---- server/routers/external.ts | 7 + server/routers/integration.ts | 12 +- .../routers/resource/getResourceAuthInfo.ts | 28 ++- server/routers/resource/index.ts | 1 + .../routers/resource/setResourceHeaderAuth.ts | 101 ++++++++++ .../[niceId]/authentication/page.tsx | 86 +++++++- src/components/PermissionsSelectBox.tsx | 1 + src/components/SetResourceHeaderAuthForm.tsx | 186 ++++++++++++++++++ 13 files changed, 516 insertions(+), 42 deletions(-) create mode 100644 server/routers/resource/setResourceHeaderAuth.ts create mode 100644 src/components/SetResourceHeaderAuthForm.tsx diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 45d53eaa..668be0db 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -61,6 +61,7 @@ export enum ActionsEnum { getUser = "getUser", setResourcePassword = "setResourcePassword", setResourcePincode = "setResourcePincode", + setResourceHeaderAuth = "setResourceHeaderAuth", setResourceWhitelist = "setResourceWhitelist", getResourceWhitelist = "getResourceWhitelist", generateAccessToken = "generateAccessToken", @@ -194,7 +195,6 @@ export async function checkUserActionPermission( return roleActionPermission.length > 0; - return false; } catch (error) { console.error("Error checking user action permission:", error); throw createHttpError( diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 29c14560..959f99ec 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -380,6 +380,14 @@ export const resourcePassword = pgTable("resourcePassword", { passwordHash: varchar("passwordHash").notNull() }); +export const resourceHeaderAuth = pgTable("resourceHeaderAuth", { + headerAuthId: serial("headerAuthId").primaryKey(), + resourceId: integer("resourceId") + .notNull() + .references(() => resources.resourceId, { onDelete: "cascade" }), + headerAuthHash: varchar("headerAuthHash").notNull() +}); + export const resourceAccessToken = pgTable("resourceAccessToken", { accessTokenId: varchar("accessTokenId").primaryKey(), orgId: varchar("orgId") @@ -689,6 +697,7 @@ export type UserOrg = InferSelectModel; export type ResourceSession = InferSelectModel; export type ResourcePincode = InferSelectModel; export type ResourcePassword = InferSelectModel; +export type ResourceHeaderAuth = InferSelectModel; export type ResourceOtp = InferSelectModel; export type ResourceAccessToken = InferSelectModel; export type ResourceWhitelist = InferSelectModel; diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts index f7719c50..09c465b5 100644 --- a/server/db/queries/verifySessionQueries.ts +++ b/server/db/queries/verifySessionQueries.ts @@ -6,6 +6,8 @@ import { ResourceRule, resourcePassword, resourcePincode, + resourceHeaderAuth, + ResourceHeaderAuth, resourceRules, resources, roleResources, @@ -24,6 +26,7 @@ export type ResourceWithAuth = { resource: Resource | null; pincode: ResourcePincode | null; password: ResourcePassword | null; + headerAuth: ResourceHeaderAuth | null; }; export type UserSessionWithUser = { @@ -72,6 +75,10 @@ export async function getResourceByDomain( resourcePassword, eq(resourcePassword.resourceId, resources.resourceId) ) + .leftJoin( + resourceHeaderAuth, + eq(resourceHeaderAuth.resourceId, resources.resourceId) + ) .where(eq(resources.fullDomain, domain)) .limit(1); @@ -82,7 +89,8 @@ export async function getResourceByDomain( return { resource: result.resources, pincode: result.resourcePincode, - password: result.resourcePassword + password: result.resourcePassword, + headerAuth: result.resourceHeaderAuth }; } diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 62fca8b4..327b5d93 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -513,6 +513,16 @@ export const resourcePassword = sqliteTable("resourcePassword", { passwordHash: text("passwordHash").notNull() }); +export const resourceHeaderAuth = sqliteTable("resourceHeaderAuth", { + headerAuthId: integer("headerAuthId").primaryKey({ + autoIncrement: true + }), + resourceId: integer("resourceId") + .notNull() + .references(() => resources.resourceId, { onDelete: "cascade" }), + headerAuthHash: text("headerAuthHash").notNull() +}); + export const resourceAccessToken = sqliteTable("resourceAccessToken", { accessTokenId: text("accessTokenId").primaryKey(), orgId: text("orgId") @@ -728,6 +738,7 @@ export type UserOrg = InferSelectModel; export type ResourceSession = InferSelectModel; export type ResourcePincode = InferSelectModel; export type ResourcePassword = InferSelectModel; +export type ResourceHeaderAuth = InferSelectModel; export type ResourceOtp = InferSelectModel; export type ResourceAccessToken = InferSelectModel; export type ResourceWhitelist = InferSelectModel; diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 7a0139bb..c380e679 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -7,22 +7,21 @@ import { import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; import { getResourceByDomain, - getUserSessionWithUser, - getUserOrgRole, - getRoleResourceAccess, - getUserResourceAccess, getResourceRules, - getOrgLoginPage + getRoleResourceAccess, + getUserOrgRole, + getUserResourceAccess, + getOrgLoginPage, + getUserSessionWithUser } from "@server/db/queries/verifySessionQueries"; import { LoginPage, Resource, ResourceAccessToken, + ResourceHeaderAuth, ResourcePassword, ResourcePincode, - ResourceRule, - sessions, - users + ResourceRule } from "@server/db"; import config from "@server/lib/config"; import { isIpInCidr } from "@server/lib/ip"; @@ -37,6 +36,7 @@ import { fromError } from "zod-validation-error"; import { getCountryCodeForIp, remoteGetCountryCodeForIp } from "@server/lib/geoip"; import { getOrgTierData } from "@server/routers/private/billing"; import { TierId } from "@server/lib/private/billing/tiers"; +import { verifyPassword } from "@server/auth/password"; // We'll see if this speeds anything up const cache = new NodeCache({ @@ -101,25 +101,28 @@ export async function verifyResourceSession( query } = parsedBody.data; + // Extract HTTP Basic Auth credentials if present + const clientHeaderAuth = extractBasicAuth(headers); + const clientIp = requestIp ? (() => { - logger.debug("Request IP:", { requestIp }); - if (requestIp.startsWith("[") && requestIp.includes("]")) { - // if brackets are found, extract the IPv6 address from between the brackets - const ipv6Match = requestIp.match(/\[(.*?)\]/); - if (ipv6Match) { - return ipv6Match[1]; - } - } + logger.debug("Request IP:", { requestIp }); + if (requestIp.startsWith("[") && requestIp.includes("]")) { + // if brackets are found, extract the IPv6 address from between the brackets + const ipv6Match = requestIp.match(/\[(.*?)\]/); + if (ipv6Match) { + return ipv6Match[1]; + } + } - // ivp4 - // split at last colon - const lastColonIndex = requestIp.lastIndexOf(":"); - if (lastColonIndex !== -1) { - return requestIp.substring(0, lastColonIndex); - } - return requestIp; - })() + // ivp4 + // split at last colon + const lastColonIndex = requestIp.lastIndexOf(":"); + if (lastColonIndex !== -1) { + return requestIp.substring(0, lastColonIndex); + } + return requestIp; + })() : undefined; logger.debug("Client IP:", { clientIp }); @@ -134,10 +137,11 @@ export async function verifyResourceSession( const resourceCacheKey = `resource:${cleanHost}`; let resourceData: | { - resource: Resource | null; - pincode: ResourcePincode | null; - password: ResourcePassword | null; - } + resource: Resource | null; + pincode: ResourcePincode | null; + password: ResourcePassword | null; + headerAuth: ResourceHeaderAuth | null; + } | undefined = cache.get(resourceCacheKey); if (!resourceData) { @@ -152,7 +156,7 @@ export async function verifyResourceSession( cache.set(resourceCacheKey, resourceData); } - const { resource, pincode, password } = resourceData; + const { resource, pincode, password, headerAuth } = resourceData; if (!resource) { logger.debug(`Resource not found ${cleanHost}`); @@ -209,21 +213,21 @@ export async function verifyResourceSession( headers && headers[ config.getRawConfig().server.resource_access_token_headers.id - ] && + ] && headers[ config.getRawConfig().server.resource_access_token_headers.token - ] + ] ) { const accessTokenId = headers[ config.getRawConfig().server.resource_access_token_headers .id - ]; + ]; const accessToken = headers[ config.getRawConfig().server.resource_access_token_headers .token - ]; + ]; const { valid, error, tokenItem } = await verifyResourceAccessToken( { @@ -288,6 +292,18 @@ export async function verifyResourceSession( } } + // check for HTTP Basic Auth header + if (headerAuth && clientHeaderAuth) { + if(cache.get(clientHeaderAuth)) { + logger.debug("Resource allowed because header auth is valid (cached)"); + return allowed(res); + }else if(await verifyPassword(clientHeaderAuth, headerAuth.headerAuthHash)){ + cache.set(clientHeaderAuth, clientHeaderAuth); + logger.debug("Resource allowed because header auth is valid"); + return allowed(res); + } + } + if (!sessions) { if (config.getRawConfig().app.log_failed_attempts) { logger.info( @@ -800,3 +816,25 @@ async function isIpInGeoIP(ip: string, countryCode: string): Promise { return cachedCountryCode?.toUpperCase() === countryCode.toUpperCase(); } + +function extractBasicAuth(headers: Record | undefined): string | undefined { + if (!headers || (!headers.authorization && !headers.Authorization)) { + return; + } + + const authHeader = headers.authorization || headers.Authorization; + + // Check if it's Basic Auth + if (!authHeader.startsWith("Basic ")) { + logger.debug("Authorization header is not Basic Auth"); + return; + } + + try { + // Extract the base64 encoded credentials + return authHeader.slice("Basic ".length); + + } catch (error) { + logger.debug("Basic Auth: Failed to decode credentials", { error: error instanceof Error ? error.message : "Unknown error" }); + } +} diff --git a/server/routers/external.ts b/server/routers/external.ts index d6fa4a16..3a96bb03 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -541,6 +541,13 @@ authenticated.post( resource.setResourcePincode ); +authenticated.post( + `/resource/:resourceId/header-auth`, + verifyResourceAccess, + verifyUserHasAction(ActionsEnum.setResourceHeaderAuth), + resource.setResourceHeaderAuth +); + authenticated.post( `/resource/:resourceId/whitelist`, verifyResourceAccess, diff --git a/server/routers/integration.ts b/server/routers/integration.ts index edad73b9..879075c7 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -24,8 +24,7 @@ import { verifyApiKeyIsRoot, verifyApiKeyClientAccess, verifyClientsEnabled, - verifyApiKeySiteResourceAccess, - verifyOrgAccess + verifyApiKeySiteResourceAccess } from "@server/middlewares"; import HttpCode from "@server/types/HttpCode"; import { Router } from "express"; @@ -401,6 +400,13 @@ authenticated.post( resource.setResourcePincode ); +authenticated.post( + `/resource/:resourceId/header-auth`, + verifyApiKeyResourceAccess, + verifyApiKeyHasAction(ActionsEnum.setResourceHeaderAuth), + resource.setResourceHeaderAuth +); + authenticated.post( `/resource/:resourceId/whitelist`, verifyApiKeyResourceAccess, @@ -660,4 +666,4 @@ authenticated.put( verifyApiKeyOrgAccess, verifyApiKeyHasAction(ActionsEnum.applyBlueprint), org.applyBlueprint -); \ No newline at end of file +); diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index f6c8c596..b7775251 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -1,7 +1,12 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; -import { resourcePassword, resourcePincode, resources } from "@server/db"; +import { + db, + resourceHeaderAuth, + resourcePassword, + resourcePincode, + resources +} from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -23,6 +28,7 @@ export type GetResourceAuthInfoResponse = { niceId: string; password: boolean; pincode: boolean; + headerAuth: boolean; sso: boolean; blockAccess: boolean; url: string; @@ -64,6 +70,14 @@ export async function getResourceAuthInfo( resourcePassword, eq(resourcePassword.resourceId, resources.resourceId) ) + + .leftJoin( + resourceHeaderAuth, + eq( + resourceHeaderAuth.resourceId, + resources.resourceId + ) + ) .where(eq(resources.resourceId, Number(resourceGuid))) .limit(1) : await db @@ -77,12 +91,21 @@ export async function getResourceAuthInfo( resourcePassword, eq(resourcePassword.resourceId, resources.resourceId) ) + + .leftJoin( + resourceHeaderAuth, + eq( + resourceHeaderAuth.resourceId, + resources.resourceId + ) + ) .where(eq(resources.resourceGuid, resourceGuid)) .limit(1); const resource = result?.resources; const pincode = result?.resourcePincode; const password = result?.resourcePassword; + const headerAuth = result?.resourceHeaderAuth; const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`; @@ -100,6 +123,7 @@ export async function getResourceAuthInfo( resourceName: resource.name, password: password !== null, pincode: pincode !== null, + headerAuth: headerAuth !== null, sso: resource.sso, blockAccess: resource.blockAccess, url, diff --git a/server/routers/resource/index.ts b/server/routers/resource/index.ts index 1a2e5c2d..60938342 100644 --- a/server/routers/resource/index.ts +++ b/server/routers/resource/index.ts @@ -22,3 +22,4 @@ export * from "./deleteResourceRule"; export * from "./listResourceRules"; export * from "./updateResourceRule"; export * from "./getUserResources"; +export * from "./setResourceHeaderAuth"; diff --git a/server/routers/resource/setResourceHeaderAuth.ts b/server/routers/resource/setResourceHeaderAuth.ts new file mode 100644 index 00000000..2e3b1a4a --- /dev/null +++ b/server/routers/resource/setResourceHeaderAuth.ts @@ -0,0 +1,101 @@ +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db, resourceHeaderAuth } from "@server/db"; +import { eq } from "drizzle-orm"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import { fromError } from "zod-validation-error"; +import { response } from "@server/lib"; +import logger from "@server/logger"; +import { hashPassword } from "@server/auth/password"; +import { OpenAPITags, registry } from "@server/openApi"; + +const setResourceAuthMethodsParamsSchema = z.object({ + resourceId: z.string().transform(Number).pipe(z.number().int().positive()) +}); + +const setResourceAuthMethodsBodySchema = z + .object({ + user: z.string().min(4).max(100).nullable(), + password: z.string().min(4).max(100).nullable() + }) + .strict(); + +registry.registerPath({ + method: "post", + path: "/resource/{resourceId}/header-auth", + description: + "Set or update the header authentication for a resource. If user and password is not provided, it will remove the header authentication.", + tags: [OpenAPITags.Resource], + request: { + params: setResourceAuthMethodsParamsSchema, + body: { + content: { + "application/json": { + schema: setResourceAuthMethodsBodySchema + } + } + } + }, + responses: {} +}); + +export async function setResourceHeaderAuth( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = setResourceAuthMethodsParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = setResourceAuthMethodsBodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { resourceId } = parsedParams.data; + const { user, password } = parsedBody.data; + + await db.transaction(async (trx) => { + await trx + .delete(resourceHeaderAuth) + .where(eq(resourceHeaderAuth.resourceId, resourceId)); + + if (user && password) { + const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64")); + + await trx + .insert(resourceHeaderAuth) + .values({ resourceId, headerAuthHash }); + } + }); + + return response(res, { + data: {}, + success: true, + error: false, + message: "Header Authentication set successfully", + status: HttpCode.CREATED + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index ae8e52ab..4b9e3ac1 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -13,7 +13,7 @@ import { ListResourceUsersResponse } from "@server/routers/resource"; import { Button } from "@app/components/ui/button"; -import { set, z } from "zod"; +import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { @@ -26,9 +26,10 @@ import { FormMessage } from "@app/components/ui/form"; import { ListUsersResponse } from "@server/routers/user"; -import { Binary, Key } from "lucide-react"; +import { Binary, Key, Bot } from "lucide-react"; import SetResourcePasswordForm from "../../../../../../components/SetResourcePasswordForm"; import SetResourcePincodeForm from "../../../../../../components/SetResourcePincodeForm"; +import SetResourceHeaderAuthForm from "../../../../../../components/SetResourceHeaderAuthForm"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { @@ -57,10 +58,13 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; +<<<<<<< HEAD import { Separator } from "@app/components/ui/separator"; import { build } from "@server/build"; import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; import { TierId } from "@server/lib/private/billing/tiers"; +======= +>>>>>>> 6f6c351f (Adding HTTP Header Authentication) const UsersRolesFormSchema = z.object({ roles: z.array( @@ -140,9 +144,12 @@ export default function ResourceAuthenticationPage() { useState(false); const [loadingRemoveResourcePincode, setLoadingRemoveResourcePincode] = useState(false); + const [loadingRemoveResourceHeaderAuth, setLoadingRemoveResourceHeaderAuth] = + useState(false); const [isSetPasswordOpen, setIsSetPasswordOpen] = useState(false); const [isSetPincodeOpen, setIsSetPincodeOpen] = useState(false); + const [isSetHeaderAuthOpen, setIsSetHeaderAuthOpen] = useState(false); const usersRolesForm = useForm({ resolver: zodResolver(UsersRolesFormSchema), @@ -429,6 +436,37 @@ export default function ResourceAuthenticationPage() { .finally(() => setLoadingRemoveResourcePincode(false)); } + function removeResourceHeaderAuth() { + setLoadingRemoveResourceHeaderAuth(true); + + api.post(`/resource/${resource.resourceId}/header-auth`, { + user: null, + password: null + }) + .then(() => { + toast({ + title: t("resourceHeaderAuthRemove"), + description: t("resourceHeaderAuthRemoveDescription") + }); + + updateAuthInfo({ + headerAuth: false + }); + router.refresh(); + }) + .catch((e) => { + toast({ + variant: "destructive", + title: t("resourceErrorHeaderAuthRemove"), + description: formatAxiosError( + e, + t("resourceErrorHeaderAuthRemoveDescription") + ) + }); + }) + .finally(() => setLoadingRemoveResourceHeaderAuth(false)); + } + if (pageLoading) { return <>; } @@ -463,6 +501,20 @@ export default function ResourceAuthenticationPage() { /> )} + {isSetHeaderAuthOpen && ( + { + setIsSetHeaderAuthOpen(false); + updateAuthInfo({ + headerAuth: true + }); + }} + /> + )} + @@ -778,6 +830,36 @@ export default function ResourceAuthenticationPage() { : t("pincodeAdd")}
+ + {/* Header Authentication Protection */} +
+
+ + + {t("resourceHeaderAuthProtection", { + status: authInfo.headerAuth + ? t("enabled") + : t("disabled") + })} + +
+ +
diff --git a/src/components/PermissionsSelectBox.tsx b/src/components/PermissionsSelectBox.tsx index 38723613..f7f895b3 100644 --- a/src/components/PermissionsSelectBox.tsx +++ b/src/components/PermissionsSelectBox.tsx @@ -57,6 +57,7 @@ function getActionsCategories(root: boolean) { [t('actionListAllowedResourceRoles')]: "listResourceRoles", [t('actionSetResourcePassword')]: "setResourcePassword", [t('actionSetResourcePincode')]: "setResourcePincode", + [t('actionSetResourceHeaderAuth')]: "setResourceHeaderAuth", [t('actionSetResourceEmailWhitelist')]: "setResourceWhitelist", [t('actionGetResourceEmailWhitelist')]: "getResourceWhitelist", [t('actionCreateSiteResource')]: "createSiteResource", diff --git a/src/components/SetResourceHeaderAuthForm.tsx b/src/components/SetResourceHeaderAuthForm.tsx new file mode 100644 index 00000000..b1a75543 --- /dev/null +++ b/src/components/SetResourceHeaderAuthForm.tsx @@ -0,0 +1,186 @@ +"use client"; + +import { Button } from "@app/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import { Input } from "@app/components/ui/input"; +import { toast } from "@app/hooks/useToast"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import { formatAxiosError } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { Resource } from "@server/db"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; + +const setHeaderAuthFormSchema = z.object({ + user: z.string().min(4).max(100), + password: z.string().min(4).max(100) +}); + +type SetHeaderAuthFormValues = z.infer; + +const defaultValues: Partial = { + user: "", + password: "" +}; + +type SetHeaderAuthFormProps = { + open: boolean; + setOpen: (open: boolean) => void; + resourceId: number; + onSetHeaderAuth?: () => void; +}; + +export default function SetResourceHeaderAuthForm({ + open, + setOpen, + resourceId, + onSetHeaderAuth +}: SetHeaderAuthFormProps) { + const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + const [loading, setLoading] = useState(false); + + const form = useForm({ + resolver: zodResolver(setHeaderAuthFormSchema), + defaultValues + }); + + useEffect(() => { + if (!open) { + return; + } + + form.reset(); + }, [open]); + + async function onSubmit(data: SetHeaderAuthFormValues) { + setLoading(true); + + api.post>(`/resource/${resourceId}/header-auth`, { + user: data.user, + password: data.password + }) + .catch((e) => { + toast({ + variant: "destructive", + title: t('resourceErrorHeaderAuthSetup'), + description: formatAxiosError( + e, + t('resourceErrorHeaderAuthSetupDescription') + ) + }); + }) + .then(() => { + toast({ + title: t('resourceHeaderAuthSetup'), + description: t('resourceHeaderAuthSetupDescription') + }); + + if (onSetHeaderAuth) { + onSetHeaderAuth(); + } + }) + .finally(() => setLoading(false)); + } + + return ( + <> + { + setOpen(val); + setLoading(false); + form.reset(); + }} + > + + + {t('resourceHeaderAuthSetupTitle')} + + {t('resourceHeaderAuthSetupTitleDescription')} + + + +
+ + ( + + {t('user')} + + + + + + )} + /> + ( + + {t('password')} + + + + + + )} + /> + + +
+ + + + + + +
+
+ + ); +} From 6105eea7a9261c5771fdd52a0ca9faf1920804b7 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 10:16:29 -0700 Subject: [PATCH 185/322] Fix rebase --- .../settings/resources/[niceId]/authentication/page.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index 4b9e3ac1..31cfbc5d 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -58,13 +58,10 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; -<<<<<<< HEAD import { Separator } from "@app/components/ui/separator"; import { build } from "@server/build"; import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; import { TierId } from "@server/lib/private/billing/tiers"; -======= ->>>>>>> 6f6c351f (Adding HTTP Header Authentication) const UsersRolesFormSchema = z.object({ roles: z.array( From 23f05d7f4e9be48bbe37450b1d54b5d674cfe7f2 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 10:20:01 -0700 Subject: [PATCH 186/322] Add translations to EN --- messages/en-US.json | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index ca5557bc..c27f067e 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1,4 +1,3 @@ - { "setupCreate": "Create your organization, site, and resources", "setupNewOrg": "New Organization", @@ -1725,5 +1724,20 @@ "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", "rewritePathDescription": "Optionally rewrite the path before forwarding to the target.", - "continueToApplication": "Continue to application" -} + "setResourceHeaderAuth": "setResourceHeaderAuth", + "resourceHeaderAuthRemove": "Remove Header Auth", + "resourceHeaderAuthRemoveDescription": "Header authentication removed successfully.", + "resourceErrorHeaderAuthRemove": "Failed to remove Header Authentication", + "resourceErrorHeaderAuthRemoveDescription": "Could not remove header authentication for the resource.", + "resourceHeaderAuthProtection": "Header Authentication Protection: {{status}}", + "headerAuthRemove": "Remove", + "headerAuthAdd": "Add", + "resourceErrorHeaderAuthSetup": "Failed to set Header Authentication", + "resourceErrorHeaderAuthSetupDescription": "Could not set header authentication for the resource.", + "resourceHeaderAuthSetup": "Header Authentication set successfully", + "resourceHeaderAuthSetupDescription": "Header authentication has been successfully set.", + "resourceHeaderAuthSetupTitle": "Set Header Authentication", + "resourceHeaderAuthSetupTitleDescription": "Set the basic auth credentials (username and password) to protect this resource with HTTP Header Authentication. Leave both fields blank to remove existing header authentication.", + "resourceHeaderAuthSubmit": "Set Header Authentication", + "actionSetResourceHeaderAuth": "Set Header Authentication" +} \ No newline at end of file From 2c46a37a5322948e6cd37aec78da61c0f13eddc3 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 10:31:31 -0700 Subject: [PATCH 187/322] Include in hybrid --- server/routers/private/hybrid.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/routers/private/hybrid.ts b/server/routers/private/hybrid.ts index dc55bc0d..2b59aa40 100644 --- a/server/routers/private/hybrid.ts +++ b/server/routers/private/hybrid.ts @@ -33,7 +33,9 @@ import { targets, loginPage, loginPageOrg, - LoginPage + LoginPage, + resourceHeaderAuth, + ResourceHeaderAuth } from "@server/db"; import { resources, @@ -200,6 +202,7 @@ export type ResourceWithAuth = { resource: Resource | null; pincode: ResourcePincode | null; password: ResourcePassword | null; + headerAuth: ResourceHeaderAuth | null; }; export type UserSessionWithUser = { @@ -478,6 +481,10 @@ hybridRouter.get( resourcePassword, eq(resourcePassword.resourceId, resources.resourceId) ) + .leftJoin( + resourceHeaderAuth, + eq(resourceHeaderAuth.resourceId, resources.resourceId) + ) .where(eq(resources.fullDomain, domain)) .limit(1); @@ -509,7 +516,8 @@ hybridRouter.get( const resourceWithAuth: ResourceWithAuth = { resource: result.resources, pincode: result.resourcePincode, - password: result.resourcePassword + password: result.resourcePassword, + headerAuth: result.resourceHeaderAuth }; return response(res, { From e121dd0d1d4c2d10cb8262008a7878ff7043c912 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 11:02:08 -0700 Subject: [PATCH 188/322] Add to blueprints --- server/lib/blueprints/proxyResources.ts | 54 ++++++++++++++++++++++++- server/lib/blueprints/types.ts | 4 ++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index adeb320f..7cdb2ca4 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -2,6 +2,7 @@ import { domains, orgDomains, Resource, + resourceHeaderAuth, resourcePincode, resourceRules, resourceWhitelist, @@ -122,7 +123,9 @@ export async function updateProxyResources( const healthcheckData = targetData.healthcheck; - const hcHeaders = healthcheckData?.headers ? JSON.stringify(healthcheckData.headers) : null; + const hcHeaders = healthcheckData?.headers + ? JSON.stringify(healthcheckData.headers) + : null; const [newHealthcheck] = await trx .insert(targetHealthCheck) @@ -263,6 +266,32 @@ export async function updateProxyResources( }); } + await trx + .delete(resourceHeaderAuth) + .where( + eq( + resourceHeaderAuth.resourceId, + existingResource.resourceId + ) + ); + if (resourceData.auth?.["basic-auth"]) { + const headerAuthUser = + resourceData.auth?.["basic-auth"]?.user; + const headerAuthPassword = + resourceData.auth?.["basic-auth"]?.password; + if (headerAuthUser && headerAuthPassword) { + const headerAuthHash = await hashPassword( + Buffer.from( + `${headerAuthUser}:${headerAuthPassword}` + ).toString("base64") + ); + await trx.insert(resourceHeaderAuth).values({ + resourceId: existingResource.resourceId, + headerAuthHash + }); + } + } + if (resourceData.auth?.["sso-roles"]) { const ssoRoles = resourceData.auth?.["sso-roles"]; await syncRoleResources( @@ -406,7 +435,9 @@ export async function updateProxyResources( ) .limit(1); - const hcHeaders = healthcheckData?.headers ? JSON.stringify(healthcheckData.headers) : null; + const hcHeaders = healthcheckData?.headers + ? JSON.stringify(healthcheckData.headers) + : null; const [newHealthcheck] = await trx .update(targetHealthCheck) @@ -591,6 +622,25 @@ export async function updateProxyResources( }); } + if (resourceData.auth?.["basic-auth"]) { + const headerAuthUser = resourceData.auth?.["basic-auth"]?.user; + const headerAuthPassword = + resourceData.auth?.["basic-auth"]?.password; + + if (headerAuthUser && headerAuthPassword) { + const headerAuthHash = await hashPassword( + Buffer.from( + `${headerAuthUser}:${headerAuthPassword}` + ).toString("base64") + ); + + await trx.insert(resourceHeaderAuth).values({ + resourceId: newResource.resourceId, + headerAuthHash + }); + } + } + resource = newResource; const [adminRole] = await trx diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index 54105dde..557714fd 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -41,6 +41,10 @@ export const AuthSchema = z.object({ // pincode has to have 6 digits pincode: z.number().min(100000).max(999999).optional(), password: z.string().min(1).optional(), + "basic-auth": z.object({ + user: z.string().min(1), + password: z.string().min(1) + }).optional(), "sso-enabled": z.boolean().optional().default(false), "sso-roles": z .array(z.string()) From 5a3bf2f7585da09358b329baf6bb878a79a80d48 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 11:06:41 -0700 Subject: [PATCH 189/322] Fix import issue --- server/routers/resource/setResourceHeaderAuth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/resource/setResourceHeaderAuth.ts b/server/routers/resource/setResourceHeaderAuth.ts index 2e3b1a4a..dc0d417d 100644 --- a/server/routers/resource/setResourceHeaderAuth.ts +++ b/server/routers/resource/setResourceHeaderAuth.ts @@ -5,7 +5,7 @@ import { eq } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; -import { response } from "@server/lib"; +import { response } from "@server/lib/response"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; import { OpenAPITags, registry } from "@server/openApi"; From 40a3eac704c9e908aae4ffbb437b130a10fd3955 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 11:28:26 -0700 Subject: [PATCH 190/322] Adjust tag match to exclude s. --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 111b0222..80d71b90 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -3,7 +3,7 @@ name: CI/CD Pipeline on: push: tags: - - "*" + - "[0-9]+.[0-9]+.[0-9]+" jobs: release: From b627e391ac5a0706a4c9abdcbdad18c39e46fa90 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 6 Oct 2025 11:29:34 -0700 Subject: [PATCH 191/322] Add tsc test --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52a5f04a..3d121f68 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,6 +35,9 @@ jobs: - name: Apply database migrations run: npm run db:sqlite:push + - name: Test with tsc + run: npx tsc --noEmit + - name: Start app in background run: nohup npm run dev & From e2534af40e12e30bae0ccee254b3dd0b82ead26d Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:42:24 +0200 Subject: [PATCH 192/322] Create dev-image.yml --- .github/workflows/dev-image.yml | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/dev-image.yml diff --git a/.github/workflows/dev-image.yml b/.github/workflows/dev-image.yml new file mode 100644 index 00000000..c989944e --- /dev/null +++ b/.github/workflows/dev-image.yml @@ -0,0 +1,70 @@ +name: Create Dev-Image + +on: + pull_request: + branches: + - main + - dev + types: + - opened + - synchronize + - reopened + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image SQLITE + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + push: true + tags: ${{ secrets.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} + cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache,mode=max + build-args: DATABASE=sqlite + + - name: Build and push Docker image PG + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + push: true + tags: ${{ secrets.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} + cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache-pg + cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache-pg,mode=max + build-args: DATABASE=pg + + - uses: actions/github-script@v8 + env: + REPO: https://hub.docker.com/r/${{ secrets.DOCKER_HUB_REPO }}/tags + DEV_PR: ${{ secrets.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} + DEV_PR_PG: ${{ secrets.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} + with: + script: | + const repo = process.env.REPO; + const devPr = process.env.DEV_PR; + const devPrPg = process.env.DEV_PR_PG; + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `👋 Thanks for your PR! + Dev images for this PR are now available on [docker hub](${repo}): + **SQLITE Image** + \`\`\` + ${devPr} + \`\`\` + **Postgresql Image** + \`\`\` + ${devPrPg} + \`\`\`` + }) From 62df92f63a2a7448ac83d6739b84e34a55416df4 Mon Sep 17 00:00:00 2001 From: Marvin <127591405+Lokowitz@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:37:22 +0200 Subject: [PATCH 193/322] Update dev-image.yml --- .github/workflows/dev-image.yml | 41 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dev-image.yml b/.github/workflows/dev-image.yml index c989944e..1f9ea0ae 100644 --- a/.github/workflows/dev-image.yml +++ b/.github/workflows/dev-image.yml @@ -13,6 +13,12 @@ on: jobs: docker: runs-on: ubuntu-latest + + env: + TAG_URL: https://hub.docker.com/r/${{ vars.DOCKER_HUB_REPO }}/tags + TAG: ${{ vars.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} + TAG_PG: ${{ vars.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} + steps: - name: Login to Docker Hub uses: docker/login-action@v3 @@ -28,9 +34,9 @@ jobs: with: platforms: linux/amd64 push: true - tags: ${{ secrets.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} - cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache,mode=max + tags: ${{ env.TAG }} + cache-from: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache + cache-to: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache,mode=max build-args: DATABASE=sqlite - name: Build and push Docker image PG @@ -38,33 +44,32 @@ jobs: with: platforms: linux/amd64 push: true - tags: ${{ secrets.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} - cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache-pg - cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_REPO }}:buildcache-pg,mode=max + tags: ${{ env.TAG_PG }} + cache-from: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache-pg + cache-to: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache-pg,mode=max build-args: DATABASE=pg - uses: actions/github-script@v8 - env: - REPO: https://hub.docker.com/r/${{ secrets.DOCKER_HUB_REPO }}/tags - DEV_PR: ${{ secrets.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} - DEV_PR_PG: ${{ secrets.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} with: script: | - const repo = process.env.REPO; - const devPr = process.env.DEV_PR; - const devPrPg = process.env.DEV_PR_PG; + const repoUrl = process.env.TAG_URL; + const tag = process.env.TAG; + const tagPg = process.env.TAG_PG; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `👋 Thanks for your PR! - Dev images for this PR are now available on [docker hub](${repo}): - **SQLITE Image** + Dev images for this PR are now available on [docker hub](${repoUrl}): + + **SQLITE Image:** \`\`\` - ${devPr} + ${tag} \`\`\` - **Postgresql Image** + + **Postgresql Image:** \`\`\` - ${devPrPg} + ${tagPg} \`\`\`` }) + From 5154d5d3eef40aeabfa66ee4081eb017c2197888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 01:23:26 +0000 Subject: [PATCH 194/322] Bump the dev-minor-updates group with 4 updates Bumps the dev-minor-updates group with 4 updates: [@react-email/preview-server](https://github.com/resend/react-email/tree/HEAD/packages/preview-server), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `@react-email/preview-server` from 4.2.12 to 4.3.0 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/preview-server/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/preview-server@4.3.0/packages/preview-server) Updates `@types/node` from 24.6.2 to 24.7.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `react-email` from 4.2.12 to 4.3.0 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/react-email/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/react-email@4.3.0/packages/react-email) Updates `typescript-eslint` from 8.45.0 to 8.46.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@react-email/preview-server" dependency-version: 4.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: "@types/node" dependency-version: 24.7.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: react-email dependency-version: 4.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: typescript-eslint dependency-version: 8.46.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 2693 ++++++--------------------------------------- package.json | 8 +- 2 files changed, 322 insertions(+), 2379 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4091a9f0..ebb5aab0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.2.12", + "@react-email/preview-server": "4.3.0", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", @@ -116,7 +116,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.2", + "@types/node": "24.7.0", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", @@ -129,12 +129,12 @@ "esbuild": "0.25.10", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.2.12", + "react-email": "4.3.0", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.45.0" + "typescript-eslint": "^8.46.0" } }, "node_modules/@alloc/quick-lru": { @@ -3205,16 +3205,16 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "devOptional": true, "license": "MIT", - "optional": true, "engines": { "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", "cpu": [ "arm64" ], @@ -3231,13 +3231,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", "cpu": [ "x64" ], @@ -3254,13 +3254,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.3" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", "cpu": [ "arm64" ], @@ -3275,9 +3275,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", "cpu": [ "x64" ], @@ -3292,9 +3292,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", "cpu": [ "arm" ], @@ -3309,9 +3309,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", "cpu": [ "arm64" ], @@ -3326,159 +3326,6 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", - "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-ppc64/node_modules/@img/sharp-libvips-linux-ppc64": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", @@ -3495,10 +3342,147 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", "cpu": [ "s390x" ], @@ -3515,13 +3499,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", "cpu": [ "x64" ], @@ -3538,13 +3522,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", "cpu": [ "arm64" ], @@ -3561,13 +3545,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", "cpu": [ "x64" ], @@ -3584,13 +3568,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", "cpu": [ "wasm32" ], @@ -3598,7 +3582,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.0" + "@emnapi/runtime": "^1.5.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -3614,6 +3598,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -3627,9 +3612,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", "cpu": [ "ia32" ], @@ -3647,9 +3632,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", "cpu": [ "x64" ], @@ -5663,14 +5648,14 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.6.tgz", - "integrity": "sha512-3SeJxKeO3TO1zVw1Nl++Cp0krYk6zHDHMCUXXVkosIzl6Nxcvb07EerQpyD2wXQSJ5RZajrYAmPaydU8Hk1IyQ==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -5689,18 +5674,18 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.6.tgz", - "integrity": "sha512-XOBq9VqC+mIn5hzjGdJLhQbvQeiOpV5ExNE6qMQQPvFsCT44QUcxFzYytTWVoyWg9XKfgrleKmTeEyu6aoTPhg==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.6", - "@radix-ui/react-toggle": "1.1.6", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -5718,165 +5703,6 @@ } } }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-collection": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", - "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.6.tgz", - "integrity": "sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-tooltip": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", @@ -6309,9 +6135,9 @@ } }, "node_modules/@react-email/preview-server": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.2.12.tgz", - "integrity": "sha512-FVM3h6vJQdjk5E3P8ts8zAPeZSyefcIDWGmy/Tnwl9zVlwiGcQSjGVPmAHlw1DdJnkK79OeC/VYisMlq0w6W8Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.3.0.tgz", + "integrity": "sha512-cUaSrxezCzdg2hF6PzIxVrtagLdw3z3ovHeB3y2RDkmDZpp7EeIoNyJm22Ch2S0uAqTZNAgqu67aroLn3mFC1A==", "dev": true, "license": "MIT", "dependencies": { @@ -6320,13 +6146,13 @@ "@babel/traverse": "7.27.0", "@lottiefiles/dotlottie-react": "0.13.3", "@radix-ui/colors": "3.0.0", - "@radix-ui/react-collapsible": "1.1.7", - "@radix-ui/react-dropdown-menu": "2.1.10", - "@radix-ui/react-popover": "1.1.10", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-tabs": "1.1.7", - "@radix-ui/react-toggle-group": "1.1.6", - "@radix-ui/react-tooltip": "1.2.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-dropdown-menu": "2.1.16", + "@radix-ui/react-popover": "1.1.15", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-tabs": "1.1.13", + "@radix-ui/react-toggle-group": "1.1.11", + "@radix-ui/react-tooltip": "1.2.8", "@types/node": "22.14.1", "@types/normalize-path": "3.0.2", "@types/react": "19.0.10", @@ -6334,8 +6160,8 @@ "@types/webpack": "5.28.5", "autoprefixer": "10.4.21", "clsx": "2.1.1", - "esbuild": "0.25.0", - "framer-motion": "12.23.12", + "esbuild": "0.25.10", + "framer-motion": "12.23.22", "json5": "2.2.3", "log-symbols": "4.1.0", "module-punycode": "npm:punycode@2.3.1", @@ -6346,7 +6172,7 @@ "prism-react-renderer": "2.4.1", "react": "19.0.0", "react-dom": "19.0.0", - "sharp": "0.34.1", + "sharp": "0.34.4", "socket.io-client": "4.8.1", "sonner": "2.0.3", "source-map-js": "1.2.1", @@ -6393,828 +6219,6 @@ "node": ">=6.9.0" } }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", - "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-darwin-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", - "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", - "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", - "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", - "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", - "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", - "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", - "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", - "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", - "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", - "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", - "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", - "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-s390x": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", - "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linux-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", - "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", - "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", - "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-wasm32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", - "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.5.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-ia32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", - "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@react-email/preview-server/node_modules/@img/sharp-win32-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", - "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@react-email/preview-server/node_modules/@next/env": { "version": "15.5.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", @@ -7358,522 +6362,6 @@ "node": ">= 10" } }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-arrow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", - "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-collapsible": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.7.tgz", - "integrity": "sha512-zGFsPcFJNdQa/UNd6MOgF40BS054FIGj32oOWBllixz42f+AkQg3QJ1YT9pw7vs+Ai+EgWkh839h69GEK8oH2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-collection": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", - "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", - "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.10.tgz", - "integrity": "sha512-8qnILty92BmXbxKugWX3jgEeFeMoxtdggeCCxb/aB7l34QFAKB23IhJfnwyVMbRnAUJiT5LOay4kUS22+AWuRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.10", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", - "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-menu": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.10.tgz", - "integrity": "sha512-OupA+1PrVf2H0K4jIwkDyA+rsJ7vF1y/VxLEO43dmZ68GtCjvx9K1/B/QscPZM3jIeFNK/wPd0HmiLjT36hVcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.7", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.6", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-callback-ref": "1.1.1", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-popover": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.10.tgz", - "integrity": "sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-popper": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", - "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-portal": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", - "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-presence": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", - "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.6.tgz", - "integrity": "sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-tabs": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.7.tgz", - "integrity": "sha512-sawt4HkD+6haVGjYOC3BMIiCumBpqTK6o407n6zN/6yReed2EN7bXyykNrpqg+xCfudpBUZg7Y2cJBd/x/iybA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.6", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-tooltip": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.3.tgz", - "integrity": "sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz", - "integrity": "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@react-email/preview-server/node_modules/@types/node": { "version": "22.14.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", @@ -7942,47 +6430,6 @@ "node": ">= 6" } }, - "node_modules/@react-email/preview-server/node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" - } - }, "node_modules/@react-email/preview-server/node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8056,50 +6503,6 @@ } } }, - "node_modules/@react-email/preview-server/node_modules/next/node_modules/sharp": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", - "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.0", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.4", - "@img/sharp-darwin-x64": "0.34.4", - "@img/sharp-libvips-darwin-arm64": "1.2.3", - "@img/sharp-libvips-darwin-x64": "1.2.3", - "@img/sharp-libvips-linux-arm": "1.2.3", - "@img/sharp-libvips-linux-arm64": "1.2.3", - "@img/sharp-libvips-linux-ppc64": "1.2.3", - "@img/sharp-libvips-linux-s390x": "1.2.3", - "@img/sharp-libvips-linux-x64": "1.2.3", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", - "@img/sharp-libvips-linuxmusl-x64": "1.2.3", - "@img/sharp-linux-arm": "0.34.4", - "@img/sharp-linux-arm64": "0.34.4", - "@img/sharp-linux-ppc64": "0.34.4", - "@img/sharp-linux-s390x": "0.34.4", - "@img/sharp-linux-x64": "0.34.4", - "@img/sharp-linuxmusl-arm64": "0.34.4", - "@img/sharp-linuxmusl-x64": "0.34.4", - "@img/sharp-wasm32": "0.34.4", - "@img/sharp-win32-arm64": "0.34.4", - "@img/sharp-win32-ia32": "0.34.4", - "@img/sharp-win32-x64": "0.34.4" - } - }, "node_modules/@react-email/preview-server/node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -9810,13 +8213,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", - "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", + "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~7.13.0" + "undici-types": "~7.14.0" } }, "node_modules/@types/nodemailer": { @@ -9983,16 +8386,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", - "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", + "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/type-utils": "8.45.0", - "@typescript-eslint/utils": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/type-utils": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -10006,7 +8409,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.45.0", + "@typescript-eslint/parser": "^8.46.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -10021,15 +8424,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", - "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", + "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4" }, "engines": { @@ -10045,13 +8448,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", - "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", + "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.45.0", - "@typescript-eslint/types": "^8.45.0", + "@typescript-eslint/tsconfig-utils": "^8.46.0", + "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "engines": { @@ -10066,13 +8469,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", - "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", + "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0" + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10083,9 +8486,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", - "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", + "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10099,14 +8502,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", - "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", + "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0", - "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -10123,9 +8526,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", - "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10136,15 +8539,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", - "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", + "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.45.0", - "@typescript-eslint/tsconfig-utils": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/project-service": "8.46.0", + "@typescript-eslint/tsconfig-utils": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -10216,15 +8619,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", - "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", + "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0" + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10239,12 +8642,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", - "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", + "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -11733,20 +10136,6 @@ "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -11765,17 +10154,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -13977,13 +12355,13 @@ } }, "node_modules/framer-motion": { - "version": "12.23.12", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", - "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==", + "version": "12.23.22", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", + "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", "dev": true, "license": "MIT", "dependencies": { - "motion-dom": "^12.23.12", + "motion-dom": "^12.23.21", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, @@ -14704,13 +13082,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "dev": true, - "license": "MIT" - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -16325,383 +14696,6 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/next/node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", - "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-darwin-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", - "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", - "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", - "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", - "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", - "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", - "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", - "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", - "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", - "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", - "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-linux-arm": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", - "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-linux-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", - "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-linux-s390x": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", - "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-linux-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", - "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", - "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", - "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.3" - } - }, - "node_modules/next/node_modules/@img/sharp-wasm32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", - "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.5.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-win32-ia32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", - "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/next/node_modules/@img/sharp-win32-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", - "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -16730,49 +14724,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/next/node_modules/sharp": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", - "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.0", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.4", - "@img/sharp-darwin-x64": "0.34.4", - "@img/sharp-libvips-darwin-arm64": "1.2.3", - "@img/sharp-libvips-darwin-x64": "1.2.3", - "@img/sharp-libvips-linux-arm": "1.2.3", - "@img/sharp-libvips-linux-arm64": "1.2.3", - "@img/sharp-libvips-linux-ppc64": "1.2.3", - "@img/sharp-libvips-linux-s390x": "1.2.3", - "@img/sharp-libvips-linux-x64": "1.2.3", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", - "@img/sharp-libvips-linuxmusl-x64": "1.2.3", - "@img/sharp-linux-arm": "0.34.4", - "@img/sharp-linux-arm64": "0.34.4", - "@img/sharp-linux-ppc64": "0.34.4", - "@img/sharp-linux-s390x": "0.34.4", - "@img/sharp-linux-x64": "0.34.4", - "@img/sharp-linuxmusl-arm64": "0.34.4", - "@img/sharp-linuxmusl-x64": "0.34.4", - "@img/sharp-wasm32": "0.34.4", - "@img/sharp-win32-arm64": "0.34.4", - "@img/sharp-win32-ia32": "0.34.4", - "@img/sharp-win32-x64": "0.34.4" - } - }, "node_modules/node-abi": { "version": "3.77.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", @@ -20749,9 +18700,9 @@ "license": "0BSD" }, "node_modules/react-email": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.12.tgz", - "integrity": "sha512-Cp6nYAG9uL4//X/96wDSKxGqVTXsaRMuD1ub0G0iBcfTqLel4VVre3kicq7RraplDuj+ATlqSZu47d7P2ULP5w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.3.0.tgz", + "integrity": "sha512-XFHCSfhdlO7k5q2TYGwC0HsVh5Yn13YaOdahuJEUEOfOJKHEpSP4PKg7R/RiKFoK9cDvzunhY+58pXxz0vE2zA==", "dev": true, "license": "MIT", "dependencies": { @@ -21655,16 +19606,16 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", - "dev": true, + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.7.1" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -21673,26 +19624,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.1", - "@img/sharp-darwin-x64": "0.34.1", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.1", - "@img/sharp-linux-arm64": "0.34.1", - "@img/sharp-linux-s390x": "0.34.1", - "@img/sharp-linux-x64": "0.34.1", - "@img/sharp-linuxmusl-arm64": "0.34.1", - "@img/sharp-linuxmusl-x64": "0.34.1", - "@img/sharp-wasm32": "0.34.1", - "@img/sharp-win32-ia32": "0.34.1", - "@img/sharp-win32-x64": "0.34.1" + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" } }, "node_modules/shebang-command": { @@ -21840,16 +19793,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -23239,16 +21182,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", - "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz", + "integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.45.0", - "@typescript-eslint/parser": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0", - "@typescript-eslint/utils": "8.45.0" + "@typescript-eslint/eslint-plugin": "8.46.0", + "@typescript-eslint/parser": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -23281,9 +21224,9 @@ } }, "node_modules/undici-types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", - "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", "devOptional": true, "license": "MIT" }, diff --git a/package.json b/package.json index a5033c4c..dd513bb4 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "devDependencies": { "@dotenvx/dotenvx": "1.51.0", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.2.12", + "@react-email/preview-server": "4.3.0", "@tailwindcss/postcss": "^4.1.14", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", @@ -139,7 +139,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.6.2", + "@types/node": "24.7.0", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.1.16", @@ -152,12 +152,12 @@ "esbuild": "0.25.10", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.2.12", + "react-email": "4.3.0", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", "tsx": "4.20.6", "typescript": "^5", - "typescript-eslint": "^8.45.0" + "typescript-eslint": "^8.46.0" }, "overrides": { "emblor": { From fe474b398918aff21267f50db7b6367eac3fade4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 01:28:25 +0000 Subject: [PATCH 195/322] Bump the prod-patch-updates group with 7 updates Bumps the prod-patch-updates group with 7 updates: | Package | From | To | | --- | --- | --- | | [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) | `0.5.5` | `0.5.6` | | [@react-email/render](https://github.com/resend/react-email/tree/HEAD/packages/render) | `1.3.1` | `1.3.2` | | [@simplewebauthn/browser](https://github.com/MasterKale/SimpleWebAuthn/tree/HEAD/packages/browser) | `13.2.0` | `13.2.2` | | [@simplewebauthn/server](https://github.com/MasterKale/SimpleWebAuthn/tree/HEAD/packages/server) | `13.2.1` | `13.2.2` | | [nodemailer](https://github.com/nodemailer/nodemailer) | `7.0.6` | `7.0.7` | | [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.9.2` | `5.9.3` | | [resend](https://github.com/resendlabs/resend-node) | `6.1.1` | `6.1.2` | Updates `@react-email/components` from 0.5.5 to 0.5.6 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/components/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/components@0.5.6/packages/components) Updates `@react-email/render` from 1.3.1 to 1.3.2 - [Release notes](https://github.com/resend/react-email/releases) - [Changelog](https://github.com/resend/react-email/blob/canary/packages/render/CHANGELOG.md) - [Commits](https://github.com/resend/react-email/commits/@react-email/render@1.3.2/packages/render) Updates `@simplewebauthn/browser` from 13.2.0 to 13.2.2 - [Release notes](https://github.com/MasterKale/SimpleWebAuthn/releases) - [Changelog](https://github.com/MasterKale/SimpleWebAuthn/blob/master/CHANGELOG.md) - [Commits](https://github.com/MasterKale/SimpleWebAuthn/commits/v13.2.2/packages/browser) Updates `@simplewebauthn/server` from 13.2.1 to 13.2.2 - [Release notes](https://github.com/MasterKale/SimpleWebAuthn/releases) - [Changelog](https://github.com/MasterKale/SimpleWebAuthn/blob/master/CHANGELOG.md) - [Commits](https://github.com/MasterKale/SimpleWebAuthn/commits/v13.2.2/packages/server) Updates `nodemailer` from 7.0.6 to 7.0.7 - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.6...v7.0.7) Updates `posthog-node` from 5.9.2 to 5.9.3 - [Release notes](https://github.com/PostHog/posthog-js/releases) - [Changelog](https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md) - [Commits](https://github.com/PostHog/posthog-js/commits/posthog-node@5.9.3/packages/node) Updates `resend` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/resendlabs/resend-node/releases) - [Commits](https://github.com/resendlabs/resend-node/compare/v6.1.1...v6.1.2) --- updated-dependencies: - dependency-name: "@react-email/components" dependency-version: 0.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@react-email/render" dependency-version: 1.3.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@simplewebauthn/browser" dependency-version: 13.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@simplewebauthn/server" dependency-version: 13.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: nodemailer dependency-version: 7.0.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: posthog-node dependency-version: 5.9.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: resend dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 60 +++++++++++++++++++++++------------------------ package.json | 14 +++++------ 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4091a9f0..bc8633fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,11 +33,11 @@ "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-tooltip": "^1.2.8", - "@react-email/components": "0.5.5", - "@react-email/render": "^1.2.0", + "@react-email/components": "0.5.6", + "@react-email/render": "^1.3.2", "@react-email/tailwind": "1.2.2", - "@simplewebauthn/browser": "^13.2.0", - "@simplewebauthn/server": "^13.2.1", + "@simplewebauthn/browser": "^13.2.2", + "@simplewebauthn/server": "^13.2.2", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", @@ -74,11 +74,11 @@ "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "7.0.6", + "nodemailer": "7.0.7", "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", - "posthog-node": "^5.8.4", + "posthog-node": "^5.9.3", "qrcode.react": "4.2.0", "react": "19.1.1", "react-dom": "19.1.1", @@ -87,7 +87,7 @@ "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", - "resend": "^6.1.1", + "resend": "^6.1.2", "semver": "^7.7.2", "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", @@ -6155,9 +6155,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.5.tgz", - "integrity": "sha512-+utnip1DiXTq5TQKvL8qztWy0EC3L+qdRIeJkBZXJA4WGIukbaqimWCTBGIMW19Pj+1iKvDYk2JQHEQDLiq7uQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.6.tgz", + "integrity": "sha512-3o9ellDaF3bBcVMWeos9HI0iUIT1zGygPRcn9WSfI5JREORiN6ViEJIvz5SKWEn1KPNZtw/iaW8ct7PpVyhomg==", "license": "MIT", "dependencies": { "@react-email/body": "0.1.0", @@ -6175,7 +6175,7 @@ "@react-email/link": "0.0.12", "@react-email/markdown": "0.0.15", "@react-email/preview": "0.0.13", - "@react-email/render": "1.3.1", + "@react-email/render": "1.3.2", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", "@react-email/tailwind": "1.2.2", @@ -8301,9 +8301,9 @@ } }, "node_modules/@react-email/render": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.1.tgz", - "integrity": "sha512-BOc/kanieEVyiuldFFvceriiBGBBVhe4JWWXCXE2ehLIqz+gSWD4rgCoXAGbJRnnVyyp4joPqK62bSfa88yonA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.2.tgz", + "integrity": "sha512-oq8/BD/I/YspeuBjjdLJG6xaf9tsPYk+VWu8/mX9xWbRN0t0ExKSVm9sEBL6RsCpndQA2jbY2VgPEreIrzUgqw==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -8405,15 +8405,15 @@ } }, "node_modules/@simplewebauthn/browser": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.0.tgz", - "integrity": "sha512-N3fuA1AAnTo5gCStYoIoiasPccC+xPLx2YU88Dv0GeAmPQTWHETlZQq5xZ0DgUq1H9loXMWQH5qqUjcI7BHJ1A==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz", + "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==", "license": "MIT" }, "node_modules/@simplewebauthn/server": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.1.tgz", - "integrity": "sha512-Inmfye5opZXe3HI0GaksqBnQiM7glcNySoG6DH1GgkO1Lh9dvuV4XSV9DK02DReUVX39HpcDob9nxHELjECoQw==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.2.tgz", + "integrity": "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA==", "license": "MIT", "dependencies": { "@hexagon/base64": "^1.1.27", @@ -16854,9 +16854,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz", - "integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.7.tgz", + "integrity": "sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -20398,9 +20398,9 @@ } }, "node_modules/posthog-node": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.2.tgz", - "integrity": "sha512-oU7FbFcH5cn40nhP04cBeT67zE76EiGWjKKzDvm6IOm5P83sqM0Ij0wMJQSHp+QI6ZN7MLzb+4xfMPUEZ4q6CA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.3.tgz", + "integrity": "sha512-YA32jx4MfL5uLiVU4ynNoSzRqAigG11pkbwEI3UlqyWJpWnp8Nvpzh59b8lpmm2+5Y+kJ99sKyWLGnNZ7K1ryA==", "license": "MIT", "dependencies": { "@posthog/core": "1.2.2" @@ -21264,15 +21264,15 @@ } }, "node_modules/resend": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/resend/-/resend-6.1.1.tgz", - "integrity": "sha512-qHip8WF4uB2k83vG5EfLWQo27anlHpQagljWLFSIXgbkmNYzoIoAsXctl/RZJl7tf+7uCLM/MEwPzyW5zwCJTA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.1.2.tgz", + "integrity": "sha512-C9Q+YkRe57P8MQlkHG3yatSR/B6sqBGA06Ri2DveJfkz9Vm16182FC/iHB0K6IAfmqZ4yRrFebFw1EPuktLtSg==", "license": "MIT", "engines": { "node": ">=18" }, "peerDependencies": { - "@react-email/render": "^1.1.0" + "@react-email/render": "*" }, "peerDependenciesMeta": { "@react-email/render": { diff --git a/package.json b/package.json index a5033c4c..3ab960e8 100644 --- a/package.json +++ b/package.json @@ -56,11 +56,11 @@ "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-tooltip": "^1.2.8", - "@react-email/components": "0.5.5", - "@react-email/render": "^1.2.0", + "@react-email/components": "0.5.6", + "@react-email/render": "^1.3.2", "@react-email/tailwind": "1.2.2", - "@simplewebauthn/browser": "^13.2.0", - "@simplewebauthn/server": "^13.2.1", + "@simplewebauthn/browser": "^13.2.2", + "@simplewebauthn/server": "^13.2.2", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", @@ -97,11 +97,11 @@ "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "7.0.6", + "nodemailer": "7.0.7", "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", - "posthog-node": "^5.8.4", + "posthog-node": "^5.9.3", "qrcode.react": "4.2.0", "react": "19.1.1", "react-dom": "19.1.1", @@ -110,7 +110,7 @@ "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", - "resend": "^6.1.1", + "resend": "^6.1.2", "semver": "^7.7.2", "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", From 7d0303e2beaa8d1cd7df41455a589bde38f9faa4 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 7 Oct 2025 15:06:42 -0700 Subject: [PATCH 196/322] Add postgres pool info to config --- server/db/pg/driver.ts | 13 +++++++------ server/lib/readConfigFile.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/server/db/pg/driver.ts b/server/db/pg/driver.ts index 44b210b0..23904c7e 100644 --- a/server/db/pg/driver.ts +++ b/server/db/pg/driver.ts @@ -35,11 +35,12 @@ function createDb() { } // Create connection pools instead of individual connections + const poolConfig = config.postgres.pool; const primaryPool = new Pool({ connectionString, - max: 20, - idleTimeoutMillis: 30000, - connectionTimeoutMillis: 5000, + max: poolConfig.max_connections, + idleTimeoutMillis: poolConfig.idle_timeout_ms, + connectionTimeoutMillis: poolConfig.connection_timeout_ms, }); const replicas = []; @@ -50,9 +51,9 @@ function createDb() { for (const conn of replicaConnections) { const replicaPool = new Pool({ connectionString: conn.connection_string, - max: 10, - idleTimeoutMillis: 30000, - connectionTimeoutMillis: 5000, + max: poolConfig.max_replica_connections, + idleTimeoutMillis: poolConfig.idle_timeout_ms, + connectionTimeoutMillis: poolConfig.connection_timeout_ms, }); replicas.push(DrizzlePostgres(replicaPool)); } diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index af6cd642..ea872252 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -158,7 +158,21 @@ export const configSchema = z connection_string: z.string() }) ) + .optional(), + pool: z + .object({ + max_connections: z.number().positive().optional().default(20), + max_replica_connections: z.number().positive().optional().default(10), + idle_timeout_ms: z.number().positive().optional().default(30000), + connection_timeout_ms: z.number().positive().optional().default(5000) + }) .optional() + .default({ + max_connections: 20, + max_replica_connections: 10, + idle_timeout_ms: 30000, + connection_timeout_ms: 5000 + }) }) .optional(), traefik: z From b00143ce9bab646125154b0eb722652e8a1704e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:25:17 +0000 Subject: [PATCH 197/322] Bump the prod-patch-updates group with 3 updates Bumps the prod-patch-updates group with 3 updates: [next-intl](https://github.com/amannn/next-intl), [nodemailer](https://github.com/nodemailer/nodemailer) and [semver](https://github.com/npm/node-semver). Updates `next-intl` from 4.3.9 to 4.3.11 - [Release notes](https://github.com/amannn/next-intl/releases) - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v4.3.9...v4.3.11) Updates `nodemailer` from 7.0.7 to 7.0.9 - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.7...v7.0.9) Updates `semver` from 7.7.2 to 7.7.3 - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.7.2...v7.7.3) --- updated-dependencies: - dependency-name: next-intl dependency-version: 4.3.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: nodemailer dependency-version: 7.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: semver dependency-version: 7.7.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 74 +++++++++++++++++++++++------------------------ package.json | 6 ++-- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c098fbd..c5f65c7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,11 +70,11 @@ "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", - "next-intl": "^4.3.9", + "next-intl": "^4.3.11", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "7.0.7", + "nodemailer": "7.0.9", "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", @@ -88,7 +88,7 @@ "rebuild": "0.1.2", "reodotdev": "^1.0.0", "resend": "^6.1.2", - "semver": "^7.7.2", + "semver": "^7.7.3", "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", "tailwind-merge": "3.3.1", @@ -2274,21 +2274,21 @@ "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", - "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.5.tgz", + "integrity": "sha512-1HTESOq1IUa23g1lFZEGIXsfZKZOwWmB9RROwGn+xariiQnd++wwTMvlRAbZ8wtXRHFUamJPxsKcxpSzeCvFWQ==", "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.1", + "@formatjs/intl-localematcher": "0.6.2", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", - "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", "license": "MIT", "dependencies": { "tslib": "^2.8.0" @@ -2304,23 +2304,23 @@ } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", - "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.3.tgz", + "integrity": "sha512-H/KfWSosaiDiOaW4nHe1Fn4Cgzm+oFQ8giTmB5RJzTBNSMmd+j2NVrvvZHAmlxJHcuOelzKBLjQ2EDcyH4NSWw==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "@formatjs/icu-skeleton-parser": "1.8.14", + "@formatjs/ecma402-abstract": "2.3.5", + "@formatjs/icu-skeleton-parser": "1.8.15", "tslib": "^2.8.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", - "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.15.tgz", + "integrity": "sha512-qNrKxWJmnWxin5U4A4Evy7C0rgRiNw3IqXu9OGuT31B8lDxBGl+OgT8kcq0ZVKK0gqA4l4SQB9x+SFAvLT5hcQ==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/ecma402-abstract": "2.3.5", "tslib": "^2.8.0" } }, @@ -11084,14 +11084,14 @@ } }, "node_modules/intl-messageformat": { - "version": "10.7.16", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", - "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "version": "10.7.17", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.17.tgz", + "integrity": "sha512-0Ugaf65B2J76rb31drgNF1l6bGEDkbIiYc2Glx6jaZINHnwa5kDRGy8KXYuA+/8P4G0c9prAFhfVhQJJfzUuvQ==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/ecma402-abstract": "2.3.5", "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.2", + "@formatjs/icu-messageformat-parser": "2.11.3", "tslib": "^2.8.0" } }, @@ -12541,9 +12541,9 @@ } }, "node_modules/next-intl": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.9.tgz", - "integrity": "sha512-4oSROHlgy8a5Qr2vH69wxo9F6K0uc6nZM2GNzqSe6ET79DEzOmBeSijCRzD5txcI4i+XTGytu4cxFsDXLKEDpQ==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.11.tgz", + "integrity": "sha512-kyjeGUuLBU1DqDVAzhgoltYxQ8esVqqqkq2BRKPFxTwHPT9r5P5ZHePu3esjc5B3dVeVC/yFf8ebEnaYo68q1g==", "funding": [ { "type": "individual", @@ -12554,7 +12554,7 @@ "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", - "use-intl": "^4.3.9" + "use-intl": "^4.3.11" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", @@ -12686,9 +12686,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.7.tgz", - "integrity": "sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.9.tgz", + "integrity": "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -17151,9 +17151,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -19004,9 +19004,9 @@ } }, "node_modules/use-intl": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.9.tgz", - "integrity": "sha512-bZu+h13HIgOvsoGleQtUe4E6gM49CRm+AH36KnJVB/qb1+Beo7jr7HNrR8YWH8oaOkQfGNm6vh0HTepxng8UTg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.11.tgz", + "integrity": "sha512-cSOPKwVjaB5Y22vnrVMio5qRolBADe+TYiqsW0/jLqRn6MUNRNBparEDfx12F170ruBWhzcdW6+v6j+MlWGbEQ==", "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "^2.2.0", diff --git a/package.json b/package.json index 61126ec5..45e74b7a 100644 --- a/package.json +++ b/package.json @@ -93,11 +93,11 @@ "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", - "next-intl": "^4.3.9", + "next-intl": "^4.3.11", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "7.0.7", + "nodemailer": "7.0.9", "npm": "^11.6.1", "oslo": "1.2.1", "pg": "^8.16.2", @@ -111,7 +111,7 @@ "rebuild": "0.1.2", "reodotdev": "^1.0.0", "resend": "^6.1.2", - "semver": "^7.7.2", + "semver": "^7.7.3", "stripe": "18.2.1", "swagger-ui-express": "^5.0.1", "tailwind-merge": "3.3.1", From 977404b8c352d3bdc893fe859bfbe801249073b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:29:00 +0000 Subject: [PATCH 198/322] Bump the prod-minor-updates group with 8 updates Bumps the prod-minor-updates group with 8 updates: | Package | From | To | | --- | --- | --- | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.837.0` | `3.901.0` | | [eslint](https://github.com/eslint/eslint) | `9.35.0` | `9.37.0` | | [ioredis](https://github.com/luin/ioredis) | `5.6.1` | `5.8.1` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.544.0` | `0.545.0` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.1.1` | `19.2.0` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.1.1` | `19.2.0` | | [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.62.0` | `7.64.0` | | [winston](https://github.com/winstonjs/winston) | `3.17.0` | `3.18.3` | Updates `@aws-sdk/client-s3` from 3.837.0 to 3.901.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.901.0/clients/client-s3) Updates `eslint` from 9.35.0 to 9.37.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v9.35.0...v9.37.0) Updates `ioredis` from 5.6.1 to 5.8.1 - [Release notes](https://github.com/luin/ioredis/releases) - [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md) - [Commits](https://github.com/luin/ioredis/compare/v5.6.1...v5.8.1) Updates `lucide-react` from 0.544.0 to 0.545.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.545.0/packages/lucide-react) Updates `react` from 19.1.1 to 19.2.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.0/packages/react) Updates `react-dom` from 19.1.1 to 19.2.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.0/packages/react-dom) Updates `react-hook-form` from 7.62.0 to 7.64.0 - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.62.0...v7.64.0) Updates `winston` from 3.17.0 to 3.18.3 - [Release notes](https://github.com/winstonjs/winston/releases) - [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md) - [Commits](https://github.com/winstonjs/winston/compare/v3.17.0...v3.18.3) --- updated-dependencies: - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.901.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: eslint dependency-version: 9.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: ioredis dependency-version: 5.8.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: lucide-react dependency-version: 0.545.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react dependency-version: 19.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react-dom dependency-version: 19.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react-hook-form dependency-version: 7.64.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: winston dependency-version: 3.18.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 1586 ++++++++++++++++++++++----------------------- package.json | 20 +- 2 files changed, 775 insertions(+), 831 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c098fbd..a6793d08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.837.0", + "@aws-sdk/client-s3": "3.901.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -53,7 +53,7 @@ "cors": "2.8.5", "crypto-js": "^4.2.0", "drizzle-orm": "0.44.6", - "eslint": "9.35.0", + "eslint": "9.37.0", "eslint-config-next": "15.5.4", "express": "5.1.0", "express-rate-limit": "8.1.0", @@ -62,11 +62,11 @@ "http-errors": "2.0.0", "i": "^0.3.7", "input-otp": "1.4.2", - "ioredis": "5.6.1", + "ioredis": "5.8.1", "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.544.0", + "lucide-react": "^0.545.0", "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", @@ -80,10 +80,10 @@ "pg": "^8.16.2", "posthog-node": "^5.9.3", "qrcode.react": "4.2.0", - "react": "19.1.1", - "react-dom": "19.1.1", + "react": "19.2.0", + "react-dom": "19.2.0", "react-easy-sort": "^1.7.0", - "react-hook-form": "7.62.0", + "react-hook-form": "7.64.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", @@ -95,7 +95,7 @@ "tw-animate-css": "^1.3.8", "uuid": "^13.0.0", "vaul": "1.1.2", - "winston": "3.17.0", + "winston": "3.18.3", "winston-daily-rotate-file": "5.0.0", "ws": "8.18.3", "yargs": "18.0.0", @@ -119,8 +119,8 @@ "@types/node": "24.7.0", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", - "@types/react": "19.1.16", - "@types/react-dom": "19.1.9", + "@types/react": "19.2.2", + "@types/react-dom": "19.2.1", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", @@ -379,87 +379,73 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.837.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.837.0.tgz", - "integrity": "sha512-sBjPPG30HIfNwpzWuajCDf7agb4YAxPFFpsp3kwgptJF8PEi0HzQg64bskquMzjqLC2tXsn5rKtDVpQOvs29MQ==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.901.0.tgz", + "integrity": "sha512-wyKhZ51ur1tFuguZ6PgrUsot9KopqD0Tmxw8O8P/N3suQDxFPr0Yo7Y77ezDRDZQ95Ml3C0jlvx79HCo8VxdWA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-node": "3.835.0", - "@aws-sdk/middleware-bucket-endpoint": "3.830.0", - "@aws-sdk/middleware-expect-continue": "3.821.0", - "@aws-sdk/middleware-flexible-checksums": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-location-constraint": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/middleware-ssec": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/signature-v4-multi-region": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/eventstream-serde-browser": "^4.0.4", - "@smithy/eventstream-serde-config-resolver": "^4.1.2", - "@smithy/eventstream-serde-node": "^4.0.4", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-blob-browser": "^4.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/hash-stream-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/md5-js": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.5", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-node": "3.901.0", + "@aws-sdk/middleware-bucket-endpoint": "3.901.0", + "@aws-sdk/middleware-expect-continue": "3.901.0", + "@aws-sdk/middleware-flexible-checksums": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-location-constraint": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/middleware-ssec": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/signature-v4-multi-region": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/eventstream-serde-browser": "^4.2.0", + "@smithy/eventstream-serde-config-resolver": "^4.3.0", + "@smithy/eventstream-serde-node": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-blob-browser": "^4.2.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/hash-stream-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/md5-js": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@aws-sdk/client-sesv2": { "version": "3.899.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.899.0.tgz", @@ -944,19 +930,6 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-arn-parser": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", - "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-endpoints": { "version": "3.895.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.895.0.tgz", @@ -1027,81 +1000,49 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sesv2/node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/@aws-sdk/client-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", - "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.901.0.tgz", + "integrity": "sha512-sGyDjjkJ7ppaE+bAKL/Q5IvVCxtoyBIzN+7+hWTS/mUxWJ9EOq9238IqmVIIK6sYNIzEf9yhobfMARasPYVTNg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1109,25 +1050,23 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", - "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.901.0.tgz", + "integrity": "sha512-brKAc3y64tdhyuEf+OPIUln86bRTqkLgb9xkd6kUdIeA5+qmp/N6amItQz+RN4k4O3kqkCPYnAd3LonTKluobw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1135,15 +1074,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", - "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.901.0.tgz", + "integrity": "sha512-5hAdVl3tBuARh3zX5MLJ1P/d+Kr5kXtDU3xm1pxUEF4xt2XkEEpwiX5fbkNkz2rbh3BCt2gOHsAbh6b3M7n+DA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1151,20 +1090,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", - "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.901.0.tgz", + "integrity": "sha512-Ggr7+0M6QZEsrqRkK7iyJLf4LkIAacAxHz9c4dm9hnDdU7vqrlJm6g73IxMJXWN1bIV7IxfpzB11DsRrB/oNjQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", "tslib": "^2.6.2" }, "engines": { @@ -1172,23 +1111,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", - "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.901.0.tgz", + "integrity": "sha512-zxadcDS0hNJgv8n4hFYJNOXyfjaNE1vvqIiF/JzZSQpSSYXzCd+WxXef5bQh+W3giDtRUmkvP5JLbamEFjZKyw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1196,22 +1135,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", - "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.901.0.tgz", + "integrity": "sha512-dPuFzMF7L1s/lQyT3wDxqLe82PyTH+5o1jdfseTEln64LJMl0ZMWaKX/C1UFNDxaTd35Cgt1bDbjjAWHMiKSFQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-ini": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-ini": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1219,16 +1158,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", - "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.901.0.tgz", + "integrity": "sha512-/IWgmgM3Cl1wTdJA5HqKMAojxLkYchh5kDuphApxKhupLu6Pu0JBOHU8A5GGeFvOycyaVwosod6zDduINZxe+A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1236,18 +1175,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", - "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.901.0.tgz", + "integrity": "sha512-SjmqZQHmqFSET7+6xcZgtH7yEyh5q53LN87GqwYlJZ6KJ5oNw11acUNEhUOL1xTSJEvaWqwTIkS2zqrzLcM9bw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.835.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/token-providers": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/client-sso": "3.901.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/token-providers": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1255,16 +1194,17 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", - "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.901.0.tgz", + "integrity": "sha512-NYjy/6NLxH9m01+pfpB4ql8QgAorJcu8tw69kzHwUd/ql6wUDTbC7HcXqtKlIwWjzjgj2BKL7j6SyFapgCuafA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1272,17 +1212,17 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.830.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.830.0.tgz", - "integrity": "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.901.0.tgz", + "integrity": "sha512-mPF3N6eZlVs9G8aBSzvtoxR1RZqMo1aIwR+X8BAZSkhfj55fVF2no4IfPXfdFO3I66N+zEQ8nKoB0uTATWrogQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1290,14 +1230,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", - "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.901.0.tgz", + "integrity": "sha512-bwq9nj6MH38hlJwOY9QXIDwa6lI48UsaZpaXbdD71BljEIRlxDzfB4JaYb+ZNNK7RIAdzsP/K05mJty6KJAQHw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1305,23 +1245,23 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.835.0.tgz", - "integrity": "sha512-9ezorQYlr5cQY28zWAReFhNKUTaXsi3TMvXIagMRrSeWtQ7R6TCYnt91xzHRCmFR2kp3zLI+dfoeH+wF3iCKUw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.901.0.tgz", + "integrity": "sha512-63lcKfggVUFyXhE4SsFXShCTCyh7ZHEqXLyYEL4DwX+VWtxutf9t9m3fF0TNUYDE8eEGWiRXhegj8l4FjuW+wA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1329,14 +1269,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.901.0.tgz", + "integrity": "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1344,13 +1284,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", - "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.901.0.tgz", + "integrity": "sha512-MuCS5R2ngNoYifkVt05CTULvYVWX0dvRT0/Md4jE3a0u0yMygYy31C1zorwfE/SUgAQXyLmUx8ATmPp9PppImQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1358,13 +1298,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.901.0.tgz", + "integrity": "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1372,14 +1312,15 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.901.0.tgz", + "integrity": "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1387,24 +1328,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.835.0.tgz", - "integrity": "sha512-oPebxpVf9smInHhevHh3APFZagGU+4RPwXEWv9YtYapFvsMq+8QXFvOfxfVZ/mwpe0JVG7EiJzL9/9Kobmts8Q==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.901.0.tgz", + "integrity": "sha512-prgjVC3fDT2VIlmQPiw/cLee8r4frTam9GILRUVQyDdNtshNwV3MiaSCLzzQJjKJlLgnBLNUHJCSmvUVtg+3iA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1412,13 +1353,13 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", - "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.901.0.tgz", + "integrity": "sha512-YiLLJmA3RvjL38mFLuu8fhTTGWtp2qT24VqpucgfoyziYcTgIQkJJmKi90Xp6R6/3VcArqilyRgM1+x8i/em+Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1426,17 +1367,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", - "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.901.0.tgz", + "integrity": "sha512-Zby4F03fvD9xAgXGPywyk4bC1jCbnyubMEYChLYohD+x20ULQCf+AimF/Btn7YL+hBpzh1+RmqmvZcx+RgwgNQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@smithy/core": "^3.5.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1444,48 +1385,48 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", - "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.901.0.tgz", + "integrity": "sha512-feAAAMsVwctk2Tms40ONybvpfJPLCmSdI+G+OTrNpizkGLNl6ik2Ng2RzxY6UqOfN8abqKP/DOUj1qYDRDG8ag==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1493,16 +1434,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.901.0.tgz", + "integrity": "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1510,16 +1451,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.835.0.tgz", - "integrity": "sha512-rEtJH4dIwJYlXXe5rIH+uTCQmd2VIjuaoHlDY3Dr4nxF6po6U7vKsLfybIU2tgflGVqoqYQnXsfW/kj/Rh+/ow==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.901.0.tgz", + "integrity": "sha512-2IWxbll/pRucp1WQkHi2W5E2SVPGBvk4Is923H7gpNksbVFws18ItjMM8ZpGm44cJEoy1zR5gjhLFklatpuoOw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1527,17 +1468,17 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", - "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.901.0.tgz", + "integrity": "sha512-pJEr1Ggbc/uVTDqp9IbNu9hdr0eQf3yZix3s4Nnyvmg4xmJSGAlbPC9LrNr5u3CDZoc8Z9CuLrvbP4MwYquNpQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1545,12 +1486,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", - "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1558,9 +1499,9 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", - "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1570,14 +1511,15 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.828.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", - "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.901.0.tgz", + "integrity": "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1597,27 +1539,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", + "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", - "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.901.0.tgz", + "integrity": "sha512-l59KQP5TY7vPVUfEURc7P5BJKuNg1RSsAKBQW7LHLECXjLqDUbo2SMLrexLBEoArSt6E8QOrIN0C8z/0Xk0jYw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1633,12 +1575,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", - "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.901.0.tgz", + "integrity": "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.6.0", + "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { @@ -1649,7 +1592,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -1925,12 +1867,12 @@ } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.5.tgz", - "integrity": "sha512-yKBJUnt9U2uuSZ0i1+Uh4ifeQBqqVgPC2jux99ixYW8n63f5d3O/HvsHiJm++idfKvRYsdbQHQ4tfkR3fTHHow==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", "dependencies": { - "@so-ric/colorspace": "^1.1.4", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -2158,18 +2100,21 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -2202,9 +2147,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", - "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2223,12 +2168,12 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { @@ -5404,12 +5349,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", - "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", + "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5417,9 +5362,9 @@ } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.1.0.tgz", - "integrity": "sha512-a36AtR7Q7XOhRPt6F/7HENmTWcB8kN7mDJcOFM/+FuKO6x88w8MQJfYCufMWh4fGyVkPjUh3Rrz/dnqFQdo6OQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5429,12 +5374,12 @@ } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.1.0.tgz", - "integrity": "sha512-Bnv0B3nSlfB2mPO0WgM49I/prl7+kamF042rrf3ezJ3Z4C7csPYvyYgZfXTGXwXfj1mAwDWjE/ybIf49PzFzvA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.0.tgz", + "integrity": "sha512-HNbGWdyTfSM1nfrZKQjYTvD8k086+M8s1EYkBUdGC++lhxegUp2HgNf5RIt6oOGVvsC26hBCW/11tv8KbwLn/Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^4.1.0", + "@smithy/util-base64": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5442,15 +5387,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", - "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", + "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5458,20 +5403,20 @@ } }, "node_modules/@smithy/core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.13.0.tgz", - "integrity": "sha512-BI6ALLPOKnPOU1Cjkc+1TPhOlP3JXSR/UH14JmnaLq41t3ma+IjuXrKfhycVjr5IQ0XxRh2NnQo3olp+eCVrGg==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.14.0.tgz", + "integrity": "sha512-XJ4z5FxvY/t0Dibms/+gLJrI5niRoY0BCmE02fwmPcRYFPI4KI876xaE79YGWIKnEslMbuQPsIEsoU/DXa0DoA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/uuid": "^1.0.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -5479,15 +5424,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", - "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", + "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5495,14 +5440,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz", - "integrity": "sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.0.tgz", + "integrity": "sha512-XE7CtKfyxYiNZ5vz7OvyTf1osrdbJfmUy+rbh+NLQmZumMGvY0mT0Cq1qKSfhrvLtRYzMsOBuRpi10dyI0EBPg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5510,13 +5455,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz", - "integrity": "sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.0.tgz", + "integrity": "sha512-U53p7fcrk27k8irLhOwUu+UYnBqsXNLKl1XevOpsxK3y1Lndk8R7CSiZV6FN3fYFuTPuJy5pP6qa/bjDzEkRvA==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5524,12 +5469,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz", - "integrity": "sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.0.tgz", + "integrity": "sha512-uwx54t8W2Yo9Jr3nVF5cNnkAAnMCJ8Wrm+wDlQY6rY/IrEgZS3OqagtCu/9ceIcZFQ1zVW/zbN9dxb5esuojfA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5537,13 +5482,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz", - "integrity": "sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.0.tgz", + "integrity": "sha512-yjM2L6QGmWgJjVu/IgYd6hMzwm/tf4VFX0lm8/SvGbGBwc+aFl3hOzvO/e9IJ2XI+22Tx1Zg3vRpFRs04SWFcg==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5551,13 +5496,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz", - "integrity": "sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.0.tgz", + "integrity": "sha512-C3jxz6GeRzNyGKhU7oV656ZbuHY93mrfkT12rmjDdZch142ykjn8do+VOkeRNjSGKw01p4g+hdalPYPhmMwk1g==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-codec": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5565,15 +5510,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", - "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.0.tgz", + "integrity": "sha512-BG3KSmsx9A//KyIfw+sqNmWFr1YBUr+TwpxFT7yPqAk0yyDh7oSNgzfNH7pS6OC099EGx2ltOULvumCFe8bcgw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5581,14 +5526,14 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.1.1.tgz", - "integrity": "sha512-avAtk++s1e/1VODf+rg7c9R2pB5G9y8yaJaGY4lPZI2+UIqVyuSDMikWjeWfBVmFZ3O7NpDxBbUCyGhThVUKWQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.0.tgz", + "integrity": "sha512-MWmrRTPqVKpN8NmxmJPTeQuhewTt8Chf+waB38LXHZoA02+BeWYVQ9ViAwHjug8m7lQb1UWuGqp3JoGDOWvvuA==", "license": "Apache-2.0", "dependencies": { - "@smithy/chunked-blob-reader": "^5.1.0", - "@smithy/chunked-blob-reader-native": "^4.1.0", - "@smithy/types": "^4.5.0", + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5596,14 +5541,14 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", - "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", + "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5611,13 +5556,13 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.1.1.tgz", - "integrity": "sha512-3ztT4pV0Moazs3JAYFdfKk11kYFDo4b/3R3+xVjIm6wY9YpJf+xfz+ocEnNKcWAdcmSMqi168i2EMaKmJHbJMA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.0.tgz", + "integrity": "sha512-8dELAuGv+UEjtzrpMeNBZc1sJhO8GxFVV/Yh21wE35oX4lOE697+lsMHBoUIFAUuYkTMIeu0EuJSEsH7/8Y+UQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5625,12 +5570,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", - "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", + "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5638,9 +5583,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", - "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5650,13 +5595,13 @@ } }, "node_modules/@smithy/md5-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.1.1.tgz", - "integrity": "sha512-MvWXKK743BuHjr/hnWuT6uStdKEaoqxHAQUvbKJPPZM5ZojTNFI5D+47BoQfBE5RgGlRRty05EbWA+NXDv+hIA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.0.tgz", + "integrity": "sha512-LFEPniXGKRQArFmDQ3MgArXlClFJMsXDteuQQY8WG1/zzv6gVSo96+qpkuu1oJp4MZsKrwchY0cuAoPKzEbaNA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5664,13 +5609,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", - "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", + "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5678,18 +5623,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.5.tgz", - "integrity": "sha512-DdOIpssQ5LFev7hV6GX9TMBW5ChTsQBxqgNW1ZGtJNSAi5ksd5klwPwwMY0ejejfEzwXXGqxgVO3cpaod4veiA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.0.tgz", + "integrity": "sha512-jFVjuQeV8TkxaRlcCNg0GFVgg98tscsmIrIwRFeC74TIUyLE3jmY9xgc1WXrPQYRjQNK3aRoaIk6fhFRGOIoGw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.13.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-middleware": "^4.1.1", + "@smithy/core": "^3.14.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5697,19 +5642,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.3.1.tgz", - "integrity": "sha512-aH2bD1bzb6FB04XBhXA5mgedEZPKx3tD/qBuYCAKt5iieWvWO1Y2j++J9uLqOndXb9Pf/83Xka/YjSnMbcPchA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.0.tgz", + "integrity": "sha512-yaVBR0vQnOnzex45zZ8ZrPzUnX73eUC8kVFaAAbn04+6V7lPtxn56vZEBBAhgS/eqD6Zm86o6sJs6FuQVoX5qg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/service-error-classification": "^4.1.2", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/uuid": "^1.0.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -5717,13 +5662,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", - "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", + "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5731,12 +5676,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", - "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", + "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5744,14 +5689,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", - "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", + "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5759,15 +5704,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", - "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", + "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5775,12 +5720,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", - "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", + "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5788,12 +5733,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", - "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", + "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5801,13 +5746,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", - "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", + "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-uri-escape": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5815,12 +5760,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", - "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", + "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5828,24 +5773,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.2.tgz", - "integrity": "sha512-Kqd8wyfmBWHZNppZSMfrQFpc3M9Y/kjyN8n8P4DqJJtuwgK1H914R471HTw7+RL+T7+kI1f1gOnL7Vb5z9+NgQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", + "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0" + "@smithy/types": "^4.6.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", - "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", + "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5853,18 +5798,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", - "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", + "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-uri-escape": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5872,17 +5817,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.5.tgz", - "integrity": "sha512-6J2hhuWu7EjnvLBIGltPCqzNswL1cW/AkaZx6i56qLsQ0ix17IAhmDD9aMmL+6CN9nCJODOXpBTCQS6iKAA7/g==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.0.tgz", + "integrity": "sha512-3BDx/aCCPf+kkinYf5QQhdQ9UAGihgOVqI3QO5xQfSaIWvUE4KYLtiGRWsNe1SR7ijXC0QEPqofVp5Sb0zC8xQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.13.0", - "@smithy/middleware-endpoint": "^4.2.5", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "@smithy/core": "^3.14.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", "tslib": "^2.6.2" }, "engines": { @@ -5890,9 +5835,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", - "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", + "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5902,13 +5847,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", - "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", + "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/querystring-parser": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -5916,13 +5861,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", - "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.2.0.tgz", + "integrity": "sha512-+erInz8WDv5KPe7xCsJCp+1WCjSbah9gWcmUXc9NqmhyPx59tf7jqFz+za1tRG1Y5KM1Cy1rWCcGypylFp4mvA==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5930,9 +5875,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", - "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5942,9 +5887,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", - "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.0.tgz", + "integrity": "sha512-U8q1WsSZFjXijlD7a4wsDQOvOwV+72iHSfq1q7VD+V75xP/pdtm0WIGuaFJ3gcADDOKj2MIBn4+zisi140HEnQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5954,12 +5899,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", - "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -5967,9 +5912,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", - "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5979,14 +5924,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.5.tgz", - "integrity": "sha512-FGBhlmFZVSRto816l6IwrmDcQ9pUYX6ikdR1mmAhdtSS1m77FgADukbQg7F7gurXfAvloxE/pgsrb7SGja6FQA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.2.0.tgz", + "integrity": "sha512-qzHp7ZDk1Ba4LDwQVCNp90xPGqSu7kmL7y5toBpccuhi3AH7dcVBIT/pUxYcInK4jOy6FikrcTGq5wxcka8UaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -5995,17 +5940,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.5.tgz", - "integrity": "sha512-Gwj8KLgJ/+MHYjVubJF0EELEh9/Ir7z7DFqyYlwgmp4J37KE+5vz6b3pWUnSt53tIe5FjDfVjDmHGYKjwIvW0Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.0.tgz", + "integrity": "sha512-FxUHS3WXgx3bTWR6yQHNHHkQHZm/XKIi/CchTnKvBulN6obWpcbzJ6lDToXn+Wp0QlVKd7uYAz2/CTw1j7m+Kg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.2.2", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -6013,13 +5958,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", - "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", + "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -6027,9 +5972,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", - "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6039,12 +5984,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", - "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", + "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -6052,13 +5997,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.2.tgz", - "integrity": "sha512-NCgr1d0/EdeP6U5PSZ9Uv5SMR5XRRYoVr1kRVtKZxWL3tixEL3UatrPIMFZSKwHlCcp2zPLDvMubVDULRqeunA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", + "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.1.2", - "@smithy/types": "^4.5.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -6066,18 +6011,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.2.tgz", - "integrity": "sha512-Ka+FA2UCC/Q1dEqUanCdpqwxOFdf5Dg2VXtPtB1qxLcSGh5C1HdzklIt18xL504Wiy9nNUKwDMRTVCbKGoK69g==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.4.0.tgz", + "integrity": "sha512-vtO7ktbixEcrVzMRmpQDnw/Ehr9UWjBvSJ9fyAbadKkC4w5Cm/4lMO8cHz8Ysb8uflvQUNRcuux/oNHKPXkffg==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -6085,9 +6030,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", - "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6097,12 +6042,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", - "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -6110,13 +6055,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.1.1.tgz", - "integrity": "sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.0.tgz", + "integrity": "sha512-0Z+nxUU4/4T+SL8BCNN4ztKdQjToNvUYmkF1kXO5T7Yz3Gafzh0HeIG6mrkN8Fz3gn9hSyxuAT+6h4vM+iQSBQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -6124,9 +6069,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.0.0.tgz", - "integrity": "sha512-OlA/yZHh0ekYFnbUkmYBDQPE6fGfdrvgz39ktp8Xf+FA6BfxLejPTMDOG0Nfk5/rDySAz1dRbFf24zaAFYVXlQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6136,61 +6081,15 @@ } }, "node_modules/@so-ric/colorspace": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.5.tgz", - "integrity": "sha512-m2yI81kZ+2J2psTYtmBs3lfl/LM4WgLMCzkweMfQdbbyndcyOmFa6pblHjvjfblphPaGDzTDK+lmC/dUMSnv0A==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "license": "MIT", "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, - "node_modules/@so-ric/colorspace/node_modules/color": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", - "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", - "license": "MIT", - "dependencies": { - "color-convert": "^3.0.1", - "color-string": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-convert": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", - "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", - "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-string": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", - "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -6592,9 +6491,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz", - "integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", + "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6602,13 +6501,13 @@ } }, "node_modules/@types/react-dom": { - "version": "19.1.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", - "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.1.tgz", + "integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==", "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/semver": { @@ -6658,12 +6557,6 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, "node_modules/@types/webpack": { "version": "5.28.5", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", @@ -8230,6 +8123,19 @@ "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/color": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -8248,6 +8154,48 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -9536,19 +9484,19 @@ } }, "node_modules/eslint": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", - "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.35.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -10140,22 +10088,18 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -11096,12 +11040,12 @@ } }, "node_modules/ioredis": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", - "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.1.tgz", + "integrity": "sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ==", "license": "MIT", "dependencies": { - "@ioredis/commands": "^1.1.1", + "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", @@ -12107,9 +12051,9 @@ } }, "node_modules/lucide-react": { - "version": "0.544.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", - "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", + "version": "0.545.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz", + "integrity": "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -16312,24 +16256,24 @@ } }, "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.0" } }, "node_modules/react-easy-sort": { @@ -16619,9 +16563,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.62.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", - "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz", + "integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -17076,9 +17020,9 @@ "license": "MIT" }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/schema-utils": { @@ -18056,9 +18000,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", "funding": [ { "type": "github", @@ -19330,13 +19274,13 @@ } }, "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", diff --git a/package.json b/package.json index 61126ec5..7b93641b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.837.0", + "@aws-sdk/client-s3": "3.901.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -76,7 +76,7 @@ "cors": "2.8.5", "crypto-js": "^4.2.0", "drizzle-orm": "0.44.6", - "eslint": "9.35.0", + "eslint": "9.37.0", "eslint-config-next": "15.5.4", "express": "5.1.0", "express-rate-limit": "8.1.0", @@ -85,11 +85,11 @@ "http-errors": "2.0.0", "i": "^0.3.7", "input-otp": "1.4.2", - "ioredis": "5.6.1", + "ioredis": "5.8.1", "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.544.0", + "lucide-react": "^0.545.0", "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", @@ -103,10 +103,10 @@ "pg": "^8.16.2", "posthog-node": "^5.9.3", "qrcode.react": "4.2.0", - "react": "19.1.1", - "react-dom": "19.1.1", + "react": "19.2.0", + "react-dom": "19.2.0", "react-easy-sort": "^1.7.0", - "react-hook-form": "7.62.0", + "react-hook-form": "7.64.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", @@ -118,7 +118,7 @@ "tw-animate-css": "^1.3.8", "uuid": "^13.0.0", "vaul": "1.1.2", - "winston": "3.17.0", + "winston": "3.18.3", "winston-daily-rotate-file": "5.0.0", "ws": "8.18.3", "yargs": "18.0.0", @@ -142,8 +142,8 @@ "@types/node": "24.7.0", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", - "@types/react": "19.1.16", - "@types/react-dom": "19.1.9", + "@types/react": "19.2.2", + "@types/react-dom": "19.2.1", "@types/semver": "^7.7.1", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", From 9a808dc1396f9a73f43462d407233d9b8563b377 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Tue, 7 Oct 2025 20:10:07 -0700 Subject: [PATCH 199/322] fix invite flow --- messages/en-US.json | 3 +- src/app/invite/page.tsx | 66 ++---------- src/components/InviteStatusCard.tsx | 158 ++++++++++++++++++++-------- 3 files changed, 125 insertions(+), 102 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index ca5557bc..e4b2999c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1725,5 +1725,6 @@ "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", "rewritePathDescription": "Optionally rewrite the path before forwarding to the target.", - "continueToApplication": "Continue to application" + "continueToApplication": "Continue to application", + "checkingInvite": "Checking Invite" } diff --git a/src/app/invite/page.tsx b/src/app/invite/page.tsx index 49c5a2c5..2e027f77 100644 --- a/src/app/invite/page.tsx +++ b/src/app/invite/page.tsx @@ -1,11 +1,6 @@ -import { internal } from "@app/lib/api"; -import { authCookieHeader } from "@app/lib/api/cookies"; import { verifySession } from "@app/lib/auth/verifySession"; -import { AcceptInviteResponse } from "@server/routers/user"; -import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import InviteStatusCard from "../../components/InviteStatusCard"; -import { formatAxiosError } from "@app/lib/api"; import { getTranslations } from "next-intl/server"; export default async function InvitePage(props: { @@ -27,8 +22,8 @@ export default async function InvitePage(props: { if (parts.length !== 2) { return ( <> -

{t('inviteInvalid')}

-

{t('inviteInvalidDescription')}

+

{t("inviteInvalid")}

+

{t("inviteInvalidDescription")}

); } @@ -36,58 +31,15 @@ export default async function InvitePage(props: { const inviteId = parts[0]; const token = parts[1]; - let error = ""; - const res = await internal - .post>( - `/invite/accept`, - { - inviteId, - token, - }, - await authCookieHeader() - ) - .catch((e) => { - error = formatAxiosError(e); - console.error(error); - }); - - if (res && res.status === 200) { - redirect(`/${res.data.data.orgId}`); - } - - function cardType() { - if (error.includes("Invite is not for this user")) { - return "wrong_user"; - } else if ( - error.includes("User does not exist. Please create an account first.") - ) { - return "user_does_not_exist"; - } else if (error.includes("You must be logged in to accept an invite")) { - return "not_logged_in"; - } else { - return "rejected"; - } - } - - const type = cardType(); - - if (!user && type === "user_does_not_exist") { - const redirectUrl = emailParam - ? `/auth/signup?redirect=/invite?token=${params.token}&email=${encodeURIComponent(emailParam)}` - : `/auth/signup?redirect=/invite?token=${params.token}`; - redirect(redirectUrl); - } - - if (!user && type === "not_logged_in") { - const redirectUrl = emailParam - ? `/auth/login?redirect=/invite?token=${params.token}&email=${encodeURIComponent(emailParam)}` - : `/auth/login?redirect=/invite?token=${params.token}`; - redirect(redirectUrl); - } - return ( <> - + ); } diff --git a/src/components/InviteStatusCard.tsx b/src/components/InviteStatusCard.tsx index 6d7db4dc..d394bd57 100644 --- a/src/components/InviteStatusCard.tsx +++ b/src/components/InviteStatusCard.tsx @@ -1,47 +1,119 @@ "use client"; -import { createApiClient } from "@app/lib/api"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; import { Button } from "@app/components/ui/button"; import { Card, CardContent, CardFooter, CardHeader, - CardTitle, + CardTitle } from "@app/components/ui/card"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { XCircle } from "lucide-react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; +import { useEffect, useState } from "react"; +import { AxiosResponse } from "axios"; +import { AcceptInviteResponse, GetUserResponse } from "@server/routers/user"; +import { Loader2 } from "lucide-react"; type InviteStatusCardProps = { - type: "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in"; - token: string; + user: GetUserResponse | null; + tokenParam: string; + inviteId: string; + inviteToken: string; email?: string; }; export default function InviteStatusCard({ - type, - token, + inviteId, email, + user, + tokenParam, + inviteToken }: InviteStatusCardProps) { const router = useRouter(); const api = createApiClient(useEnvContext()); const t = useTranslations(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [type, setType] = useState< + "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in" + >("rejected"); + + useEffect(() => { + async function init() { + let error = ""; + const res = await api + .post>(`/invite/accept`, { + inviteId, + token: inviteToken + }) + .catch((e) => { + error = formatAxiosError(e); + console.log("Error accepting invite:", error); + setError(error); + // console.error(e); + }); + + if (res && res.status === 200) { + router.push(`/${res.data.data.orgId}`); + return; + } + + function cardType() { + if (error.includes("Invite is not for this user")) { + return "wrong_user"; + } else if ( + error.includes( + "User does not exist. Please create an account first." + ) + ) { + return "user_does_not_exist"; + } else if ( + error.includes("You must be logged in to accept an invite") + ) { + return "not_logged_in"; + } else { + return "rejected"; + } + } + + const type = cardType(); + setType(type); + + if (!user && type === "user_does_not_exist") { + const redirectUrl = email + ? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/signup?redirect=/invite?token=${tokenParam}`; + router.push(redirectUrl); + } else if (!user && type === "not_logged_in") { + const redirectUrl = email + ? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/login?redirect=/invite?token=${tokenParam}`; + router.push(redirectUrl); + } else { + setLoading(false); + } + } + + init(); + }, []); + async function goToLogin() { await api.post("/auth/logout", {}); - const redirectUrl = email - ? `/auth/login?redirect=/invite?token=${token}&email=${encodeURIComponent(email)}` - : `/auth/login?redirect=/invite?token=${token}`; + const redirectUrl = email + ? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/login?redirect=/invite?token=${tokenParam}`; router.push(redirectUrl); } async function goToSignup() { await api.post("/auth/logout", {}); - const redirectUrl = email - ? `/auth/signup?redirect=/invite?token=${token}&email=${encodeURIComponent(email)}` - : `/auth/signup?redirect=/invite?token=${token}`; + const redirectUrl = email + ? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/signup?redirect=/invite?token=${tokenParam}`; router.push(redirectUrl); } @@ -50,35 +122,27 @@ export default function InviteStatusCard({ return (

- {t('inviteErrorNotValid')} + {t("inviteErrorNotValid")}

    -
  • {t('inviteErrorExpired')}
  • -
  • {t('inviteErrorRevoked')}
  • -
  • {t('inviteErrorTypo')}
  • +
  • {t("inviteErrorExpired")}
  • +
  • {t("inviteErrorRevoked")}
  • +
  • {t("inviteErrorTypo")}
); } else if (type === "wrong_user") { return (
-

- {t('inviteErrorUser')} -

-

- {t('inviteLoginUser')} -

+

{t("inviteErrorUser")}

+

{t("inviteLoginUser")}

); } else if (type === "user_does_not_exist") { return (
-

- {t('inviteErrorNoUser')} -

-

- {t('inviteCreateUser')} -

+

{t("inviteErrorNoUser")}

+

{t("inviteCreateUser")}

); } @@ -92,37 +156,43 @@ export default function InviteStatusCard({ router.push("/"); }} > - {t('goHome')} + {t("goHome")} ); } else if (type === "wrong_user") { return ( - + ); } else if (type === "user_does_not_exist") { - return ; + return ; } } return ( -
+
- {/*
-
*/} - {t('inviteNotAccepted')} + {loading ? t("checkingInvite") : t("inviteNotAccepted")}
- {renderBody()} + + {loading && ( +
+
+ + {t("loading")} +
+
+ )} + {!loading && renderBody()} +
- - {renderFooter()} - + {!loading && ( + + {renderFooter()} + + )}
); From c5b3d92466706dce6f63237d3378d7302a34f91b Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 7 Oct 2025 21:11:29 -0700 Subject: [PATCH 200/322] Update lock --- package-lock.json | 41744 +++++++++++++++++++++++--------------------- 1 file changed, 22002 insertions(+), 19742 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65e397a6..cb110cd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19751 +1,22011 @@ { - "name": "@fosrl/pangolin", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@fosrl/pangolin", - "version": "0.0.0", - "license": "SEE LICENSE IN LICENSE AND README.md", - "dependencies": { - "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.837.0", - "@hookform/resolvers": "5.2.2", - "@node-rs/argon2": "^2.0.2", - "@oslojs/crypto": "1.0.1", - "@oslojs/encoding": "1.1.0", - "@radix-ui/react-avatar": "1.1.10", - "@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-icons": "1.3.2", - "@radix-ui/react-label": "2.1.7", - "@radix-ui/react-popover": "1.1.15", - "@radix-ui/react-progress": "^1.1.7", - "@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.7", - "@radix-ui/react-slot": "1.2.3", - "@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", - "@react-email/components": "0.5.6", - "@react-email/render": "^1.3.2", - "@react-email/tailwind": "1.2.2", - "@simplewebauthn/browser": "^13.2.2", - "@simplewebauthn/server": "^13.2.2", - "@tailwindcss/forms": "^0.5.10", - "@tanstack/react-table": "8.21.3", - "arctic": "^3.7.0", - "axios": "^1.12.2", - "better-sqlite3": "11.7.0", - "canvas-confetti": "1.9.3", - "class-variance-authority": "^0.7.1", - "clsx": "2.1.1", - "cmdk": "1.1.1", - "cookie": "^1.0.2", - "cookie-parser": "1.4.7", - "cookies": "^0.9.1", - "cors": "2.8.5", - "crypto-js": "^4.2.0", - "drizzle-orm": "0.44.6", - "eslint": "9.35.0", - "eslint-config-next": "15.5.4", - "express": "5.1.0", - "express-rate-limit": "8.1.0", - "glob": "11.0.3", - "helmet": "8.1.0", - "http-errors": "2.0.0", - "i": "^0.3.7", - "input-otp": "1.4.2", - "ioredis": "5.6.1", - "jmespath": "^0.16.0", - "js-yaml": "4.1.0", - "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.544.0", - "maxmind": "5.0.0", - "moment": "2.30.1", - "next": "15.5.4", - "next-intl": "^4.3.9", - "next-themes": "0.4.6", - "node-cache": "5.1.2", - "node-fetch": "3.3.2", - "nodemailer": "7.0.7", - "npm": "^11.6.1", - "oslo": "1.2.1", - "pg": "^8.16.2", - "posthog-node": "^5.9.3", - "qrcode.react": "4.2.0", - "react": "19.1.1", - "react-dom": "19.1.1", - "react-easy-sort": "^1.7.0", - "react-hook-form": "7.62.0", - "react-icons": "^5.5.0", - "rebuild": "0.1.2", - "reodotdev": "^1.0.0", - "resend": "^6.1.2", - "semver": "^7.7.2", - "stripe": "18.2.1", - "swagger-ui-express": "^5.0.1", - "tailwind-merge": "3.3.1", - "tw-animate-css": "^1.3.8", - "uuid": "^13.0.0", - "vaul": "1.1.2", - "winston": "3.17.0", - "winston-daily-rotate-file": "5.0.0", - "ws": "8.18.3", - "yargs": "18.0.0", - "zod": "3.25.76", - "zod-validation-error": "3.5.2" - }, - "devDependencies": { - "@dotenvx/dotenvx": "1.51.0", - "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/preview-server": "4.3.0", - "@tailwindcss/postcss": "^4.1.14", - "@types/better-sqlite3": "7.6.12", - "@types/cookie-parser": "1.4.9", - "@types/cors": "2.8.19", - "@types/crypto-js": "^4.2.2", - "@types/express": "5.0.3", - "@types/express-session": "^1.18.2", - "@types/jmespath": "^0.15.2", - "@types/js-yaml": "4.0.9", - "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.7.0", - "@types/nodemailer": "7.0.2", - "@types/pg": "8.15.5", - "@types/react": "19.1.16", - "@types/react-dom": "19.1.9", - "@types/semver": "^7.7.1", - "@types/swagger-ui-express": "^4.1.8", - "@types/ws": "8.18.1", - "@types/yargs": "17.0.33", - "drizzle-kit": "0.31.5", - "esbuild": "0.25.10", - "esbuild-node-externals": "1.18.0", - "postcss": "^8", - "react-email": "4.3.0", - "tailwindcss": "^4.1.4", - "tsc-alias": "1.8.16", - "tsx": "4.20.6", - "typescript": "^5", - "typescript-eslint": "^8.46.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@asteasolutions/zod-to-openapi": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz", - "integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==", - "license": "MIT", - "dependencies": { - "openapi3-ts": "^4.1.2" - }, - "peerDependencies": { - "zod": "^3.20.2" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.837.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.837.0.tgz", - "integrity": "sha512-sBjPPG30HIfNwpzWuajCDf7agb4YAxPFFpsp3kwgptJF8PEi0HzQg64bskquMzjqLC2tXsn5rKtDVpQOvs29MQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-node": "3.835.0", - "@aws-sdk/middleware-bucket-endpoint": "3.830.0", - "@aws-sdk/middleware-expect-continue": "3.821.0", - "@aws-sdk/middleware-flexible-checksums": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-location-constraint": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/middleware-ssec": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/signature-v4-multi-region": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/eventstream-serde-browser": "^4.0.4", - "@smithy/eventstream-serde-config-resolver": "^4.1.2", - "@smithy/eventstream-serde-node": "^4.0.4", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-blob-browser": "^4.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/hash-stream-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/md5-js": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.5", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-s3/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@aws-sdk/client-sesv2": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.899.0.tgz", - "integrity": "sha512-aMs3QgB9lWaKKrnx9KhIopoeXLNzI/sqdp5M56j30jlBD4vqdcCzW2OwFAAs26QzUgNKOOSY+iLZcE9DUDdIvg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.899.0", - "@aws-sdk/credential-provider-node": "3.899.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.899.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/signature-v4-multi-region": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.899.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.13.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.5", - "@smithy/middleware-retry": "^4.3.1", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.5", - "@smithy/util-defaults-mode-node": "^4.1.5", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/client-sso": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.899.0.tgz", - "integrity": "sha512-EKz/iiVDv2OC8/3ONcXG3+rhphx9Heh7KXQdsZzsAXGVn6mWtrHQLrWjgONckmK4LrD07y4+5WlJlGkMxSMA5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.899.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.899.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.899.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.13.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.5", - "@smithy/middleware-retry": "^4.3.1", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.5", - "@smithy/util-defaults-mode-node": "^4.1.5", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/core": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.899.0.tgz", - "integrity": "sha512-Enp5Zw37xaRlnscyaelaUZNxVqyE3CTS8gjahFbW2bbzVtRD2itHBVgq8A3lvKiFb7Feoxa71aTe0fQ1I6AhQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.13.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.899.0.tgz", - "integrity": "sha512-wXQ//KQ751EFhUbdfoL/e2ZDaM8l2Cff+hVwFcj32yiZyeCMhnoLRMQk2euAaUOugqPY5V5qesFbHhISbIedtw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.899.0.tgz", - "integrity": "sha512-/rRHyJFdnPrupjt/1q/PxaO6O26HFsguVUJSUeMeGUWLy0W8OC3slLFDNh89CgTqnplCyt1aLFMCagRM20HjNQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.899.0.tgz", - "integrity": "sha512-B8oFNFTDV0j1yiJiqzkC2ybml+theNnmsLrTLBhJbnBLWkxEcmVGKVIMnATW9BUCBhHmEtDiogdNIzSwP8tbMw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/credential-provider-env": "3.899.0", - "@aws-sdk/credential-provider-http": "3.899.0", - "@aws-sdk/credential-provider-process": "3.899.0", - "@aws-sdk/credential-provider-sso": "3.899.0", - "@aws-sdk/credential-provider-web-identity": "3.899.0", - "@aws-sdk/nested-clients": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.899.0.tgz", - "integrity": "sha512-nHBnZ2ZCOqTGJ2A9xpVj8iK6+WV+j0JNv3XGEkIuL4mqtGEPJlEex/0mD/hqc1VF8wZzojji2OQ3892m1mUOSA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.899.0", - "@aws-sdk/credential-provider-http": "3.899.0", - "@aws-sdk/credential-provider-ini": "3.899.0", - "@aws-sdk/credential-provider-process": "3.899.0", - "@aws-sdk/credential-provider-sso": "3.899.0", - "@aws-sdk/credential-provider-web-identity": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.899.0.tgz", - "integrity": "sha512-1PWSejKcJQUKBNPIqSHlEW4w8vSjmb+3kNJqCinJybjp5uP5BJgBp6QNcb8Nv30VBM0bn3ajVd76LCq4ZshQAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.899.0.tgz", - "integrity": "sha512-URlMbo74CAhIGrhzEP2fw5F5Tt6MRUctA8aa88MomlEHCEbJDsMD3nh6qoXxwR3LyvEBFmCWOZ/1TWmAjMsSdA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.899.0", - "@aws-sdk/core": "3.899.0", - "@aws-sdk/token-providers": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.899.0.tgz", - "integrity": "sha512-UEn5o5FMcbeFPRRkJI6VCrgdyR9qsLlGA7+AKCYuYADsKbvJGIIQk6A2oD82vIVvLYD3TtbTLDLsF7haF9mpbw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/nested-clients": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.893.0.tgz", - "integrity": "sha512-qL5xYRt80ahDfj9nDYLhpCNkDinEXvjLe/Qen/Y/u12+djrR2MB4DRa6mzBCkLkdXDtf0WAoW2EZsNCfGrmOEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-logger": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.893.0.tgz", - "integrity": "sha512-ZqzMecjju5zkBquSIfVfCORI/3Mge21nUY4nWaGQy+NUXehqCGG4W7AiVpiHGOcY2cGJa7xeEkYcr2E2U9U0AA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.893.0.tgz", - "integrity": "sha512-H7Zotd9zUHQAr/wr3bcWHULYhEeoQrF54artgsoUGIf/9emv6LzY89QUccKIxYd6oHKNTrTyXm9F0ZZrzXNxlg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws/lambda-invoke-store": "^0.0.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.899.0.tgz", - "integrity": "sha512-/3/EIRSwQ5CNOSTHx96gVGzzmTe46OxcPG5FTgM6i9ZD+K/Q3J/UPGFL5DPzct5fXiSLvD1cGQitWHStVDjOVQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.13.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.899.0.tgz", - "integrity": "sha512-6EsVCC9j1VIyVyLOg+HyO3z9L+c0PEwMiHe3kuocoMf8nkfjSzJfIl6zAtgAXWgP5MKvusTP2SUbS9ezEEHZ+A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@smithy/core": "^3.13.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/nested-clients": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.899.0.tgz", - "integrity": "sha512-ySXXsFO0RH28VISEqvCuPZ78VAkK45/+OCIJgPvYpcCX9CVs70XSvMPXDI46I49mudJ1s4H3IUKccYSEtA+jaw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.899.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.899.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.899.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.13.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.5", - "@smithy/middleware-retry": "^4.3.1", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.5", - "@smithy/util-defaults-mode-node": "^4.1.5", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.893.0.tgz", - "integrity": "sha512-/cJvh3Zsa+Of0Zbg7vl9wp/kZtdb40yk/2+XcroAMVPO9hPvmS9r/UOm6tO7FeX4TtkRFwWaQJiTZTgSdsPY+Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.899.0.tgz", - "integrity": "sha512-wV51Jogxhd7dI4Q2Y1ASbkwTsRT3G8uwWFDCwl+WaErOQAzofKlV6nFJQlfgjMk4iEn2gFOIWqJ8fMTGShRK/A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/token-providers": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.899.0.tgz", - "integrity": "sha512-Ovu1nWr8HafYa/7DaUvvPnzM/yDUGDBqaiS7rRzv++F5VwyFY37+z/mHhvRnr+PbNWo8uf22a121SNue5uwP2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.899.0", - "@aws-sdk/nested-clients": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.893.0.tgz", - "integrity": "sha512-Aht1nn5SnA0N+Tjv0dzhAY7CQbxVtmq1bBR6xI0MhG7p2XYVh1wXuKTzrldEvQWwA3odOYunAfT9aBiKZx9qIg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-arn-parser": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", - "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-endpoints": { - "version": "3.895.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.895.0.tgz", - "integrity": "sha512-MhxBvWbwxmKknuggO2NeMwOVkHOYL98pZ+1ZRI5YwckoCL3AvISMnPJgfN60ww6AIXHGpkp+HhpFdKOe8RHSEg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-endpoints": "^3.1.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.893.0.tgz", - "integrity": "sha512-PE9NtbDBW6Kgl1bG6A5fF3EPo168tnkj8TgMcT0sg4xYBWsBpq0bpJZRh+Jm5Bkwiw9IgTCLjEU7mR6xWaMB9w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.899.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.899.0.tgz", - "integrity": "sha512-CiP0UAVQWLg2+8yciUBzVLaK5Fr7jBQ7wVu+p/O2+nlCOD3E3vtL1KZ1qX/d3OVpVSVaMAdZ9nbyewGV9hvjjg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.899.0", - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.894.0.tgz", - "integrity": "sha512-E6EAMc9dT1a2DOdo4zyOf3fp5+NJ2wI+mcm7RaW1baFIWDwcb99PpvWoV7YEiK7oaBDshuOEGWKUSYXdW+JYgA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/client-sesv2/node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", - "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", - "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", - "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", - "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", - "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", - "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-ini": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", - "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", - "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.835.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/token-providers": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", - "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.830.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.830.0.tgz", - "integrity": "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", - "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.835.0.tgz", - "integrity": "sha512-9ezorQYlr5cQY28zWAReFhNKUTaXsi3TMvXIagMRrSeWtQ7R6TCYnt91xzHRCmFR2kp3zLI+dfoeH+wF3iCKUw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", - "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.835.0.tgz", - "integrity": "sha512-oPebxpVf9smInHhevHh3APFZagGU+4RPwXEWv9YtYapFvsMq+8QXFvOfxfVZ/mwpe0JVG7EiJzL9/9Kobmts8Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", - "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", - "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@smithy/core": "^3.5.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", - "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.835.0.tgz", - "integrity": "sha512-rEtJH4dIwJYlXXe5rIH+uTCQmd2VIjuaoHlDY3Dr4nxF6po6U7vKsLfybIU2tgflGVqoqYQnXsfW/kj/Rh+/ow==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", - "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", - "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", - "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.828.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", - "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", - "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", - "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", - "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", - "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.4" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.5.tgz", - "integrity": "sha512-yKBJUnt9U2uuSZ0i1+Uh4ifeQBqqVgPC2jux99ixYW8n63f5d3O/HvsHiJm++idfKvRYsdbQHQ4tfkR3fTHHow==", - "license": "MIT", - "dependencies": { - "@so-ric/colorspace": "^1.1.4", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@dotenvx/dotenvx": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.51.0.tgz", - "integrity": "sha512-CbMGzyOYSyFF7d4uaeYwO9gpSBzLTnMmSmTVpCZjvpJFV69qYbjYPpzNnCz1mb2wIvEhjWjRwQWuBzTO0jITww==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "commander": "^11.1.0", - "dotenv": "^17.2.1", - "eciesjs": "^0.4.10", - "execa": "^5.1.1", - "fdir": "^6.2.0", - "ignore": "^5.3.0", - "object-treeify": "1.1.33", - "picomatch": "^4.0.2", - "which": "^4.0.0" - }, - "bin": { - "dotenvx": "src/cli/dotenvx.js" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/@drizzle-team/brocli": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", - "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@ecies/ciphers": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.4.tgz", - "integrity": "sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==", - "dev": true, - "license": "MIT", - "engines": { - "bun": ">=1", - "deno": ">=2", - "node": ">=16" - }, - "peerDependencies": { - "@noble/ciphers": "^1.0.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild-kit/core-utils": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", - "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", - "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.18.20", - "source-map-support": "^0.5.21" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/@esbuild-kit/esm-loader": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", - "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", - "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, - "license": "MIT", - "dependencies": { - "@esbuild-kit/core-utils": "^3.3.2", - "get-tsconfig": "^4.7.0" - } - }, - "node_modules/@esbuild-plugins/tsconfig-paths": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@esbuild-plugins/tsconfig-paths/-/tsconfig-paths-0.1.2.tgz", - "integrity": "sha512-TusFR26Y+Ze+Zm+NdfqZTSG4XyrXKxIaAfYCL3jASEI/gHjSdoCujATjzNWaaXs6Sk6Bv2D7NLr4Jdz1gysy/Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "debug": "^4.3.1", - "find-up": "^5.0.0", - "strip-json-comments": "^3.1.1" - }, - "peerDependencies": { - "esbuild": "*", - "typescript": "*" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", - "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.2", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", - "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.1", - "decimal.js": "^10.4.3", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", - "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", - "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", - "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "@formatjs/icu-skeleton-parser": "1.8.14", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", - "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", - "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", - "license": "MIT", - "dependencies": { - "tslib": "2" - } - }, - "node_modules/@hexagon/base64": { - "version": "1.1.28", - "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz", - "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", - "license": "MIT" - }, - "node_modules/@hookform/resolvers": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", - "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", - "license": "MIT", - "dependencies": { - "@standard-schema/utils": "^0.3.0" - }, - "peerDependencies": { - "react-hook-form": "^7.55.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", - "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", - "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.3" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", - "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", - "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", - "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", - "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", - "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", - "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", - "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", - "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", - "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", - "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", - "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", - "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", - "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", - "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.3" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", - "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", - "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.3" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", - "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.5.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", - "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", - "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", - "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@ioredis/commands": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", - "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", - "license": "MIT" - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@levischuck/tiny-cbor": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", - "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==", - "license": "MIT" - }, - "node_modules/@lottiefiles/dotlottie-react": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.13.3.tgz", - "integrity": "sha512-V4FfdYlqzjBUX7f0KV6vfQOOI0Cp+3XeG/ZqSDFSEVg5P7fpROpDv5/I9aTM8sOCESK1SWT96Fem+QVUnBV1wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lottiefiles/dotlottie-web": "0.42.0" - }, - "peerDependencies": { - "react": "^17 || ^18 || ^19" - } - }, - "node_modules/@lottiefiles/dotlottie-web": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.42.0.tgz", - "integrity": "sha512-Zr2LCaOAoPCsdAQgeLyCSiQ1+xrAJtRCyuEYDj0qR5heUwpc+Pxbb88JyTVumcXFfKOBMOMmrlsTScLz2mrvQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@next/env": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.4.tgz", - "integrity": "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.4.tgz", - "integrity": "sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==", - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz", - "integrity": "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz", - "integrity": "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@noble/ciphers": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", - "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@node-rs/argon2": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-2.0.2.tgz", - "integrity": "sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@node-rs/argon2-android-arm-eabi": "2.0.2", - "@node-rs/argon2-android-arm64": "2.0.2", - "@node-rs/argon2-darwin-arm64": "2.0.2", - "@node-rs/argon2-darwin-x64": "2.0.2", - "@node-rs/argon2-freebsd-x64": "2.0.2", - "@node-rs/argon2-linux-arm-gnueabihf": "2.0.2", - "@node-rs/argon2-linux-arm64-gnu": "2.0.2", - "@node-rs/argon2-linux-arm64-musl": "2.0.2", - "@node-rs/argon2-linux-x64-gnu": "2.0.2", - "@node-rs/argon2-linux-x64-musl": "2.0.2", - "@node-rs/argon2-wasm32-wasi": "2.0.2", - "@node-rs/argon2-win32-arm64-msvc": "2.0.2", - "@node-rs/argon2-win32-ia32-msvc": "2.0.2", - "@node-rs/argon2-win32-x64-msvc": "2.0.2" - } - }, - "node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", - "integrity": "sha512-ZM3jrHuJ0dKOhvA80gKJqBpBRmTJTFSo2+xVZR+phQcbAKRlDMSZMFDiKbSTnctkfwNFtjgDdh5g1vaEV04AvA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-2.0.2.tgz", - "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", - "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@node-rs/bcrypt-android-arm-eabi": "1.9.0", - "@node-rs/bcrypt-android-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-x64": "1.9.0", - "@node-rs/bcrypt-freebsd-x64": "1.9.0", - "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", - "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", - "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-x64-musl": "1.9.0", - "@node-rs/bcrypt-wasm32-wasi": "1.9.0", - "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", - "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", - "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" - } - }, - "node_modules/@node-rs/bcrypt-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@oslojs/asn1": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz", - "integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==", - "license": "MIT", - "dependencies": { - "@oslojs/binary": "1.0.0" - } - }, - "node_modules/@oslojs/binary": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz", - "integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==", - "license": "MIT" - }, - "node_modules/@oslojs/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==", - "license": "MIT", - "dependencies": { - "@oslojs/asn1": "1.0.0", - "@oslojs/binary": "1.0.0" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, - "node_modules/@oslojs/jwt": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@oslojs/jwt/-/jwt-0.2.0.tgz", - "integrity": "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==", - "license": "MIT", - "dependencies": { - "@oslojs/encoding": "0.4.1" - } - }, - "node_modules/@oslojs/jwt/node_modules/@oslojs/encoding": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz", - "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==", - "license": "MIT" - }, - "node_modules/@peculiar/asn1-android": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.5.0.tgz", - "integrity": "sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-cms": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.5.0.tgz", - "integrity": "sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "@peculiar/asn1-x509-attr": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-csr": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.5.0.tgz", - "integrity": "sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.5.0.tgz", - "integrity": "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pfx": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.5.0.tgz", - "integrity": "sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.5.0", - "@peculiar/asn1-pkcs8": "^2.5.0", - "@peculiar/asn1-rsa": "^2.5.0", - "@peculiar/asn1-schema": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.5.0.tgz", - "integrity": "sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs9": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.5.0.tgz", - "integrity": "sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.5.0", - "@peculiar/asn1-pfx": "^2.5.0", - "@peculiar/asn1-pkcs8": "^2.5.0", - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "@peculiar/asn1-x509-attr": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-rsa": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.5.0.tgz", - "integrity": "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.5.0.tgz", - "integrity": "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==", - "license": "MIT", - "dependencies": { - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.5.0.tgz", - "integrity": "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509-attr": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.5.0.tgz", - "integrity": "sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/x509": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.0.tgz", - "integrity": "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.5.0", - "@peculiar/asn1-csr": "^2.5.0", - "@peculiar/asn1-ecc": "^2.5.0", - "@peculiar/asn1-pkcs9": "^2.5.0", - "@peculiar/asn1-rsa": "^2.5.0", - "@peculiar/asn1-schema": "^2.5.0", - "@peculiar/asn1-x509": "^2.5.0", - "pvtsutils": "^1.3.6", - "reflect-metadata": "^0.2.2", - "tslib": "^2.8.1", - "tsyringe": "^4.10.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@posthog/core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.2.2.tgz", - "integrity": "sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==", - "license": "MIT" - }, - "node_modules/@radix-ui/colors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", - "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", - "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", - "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", - "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-icons": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", - "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", - "license": "MIT", - "peerDependencies": { - "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", - "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", - "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-progress": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", - "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-radio-group": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", - "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", - "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-switch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", - "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toast": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", - "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", - "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", - "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-toggle": "1.1.10", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, - "node_modules/@react-email/body": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.1.0.tgz", - "integrity": "sha512-o1bcSAmDYNNHECbkeyceCVPGmVsYvT+O3sSO/Ct7apKUu3JphTi31hu+0Nwqr/pgV5QFqdoT5vdS3SW5DJFHgQ==", - "license": "MIT", - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/button": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.0.tgz", - "integrity": "sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/code-block": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", - "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/code-inline": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.5.tgz", - "integrity": "sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/column": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.13.tgz", - "integrity": "sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/components": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.6.tgz", - "integrity": "sha512-3o9ellDaF3bBcVMWeos9HI0iUIT1zGygPRcn9WSfI5JREORiN6ViEJIvz5SKWEn1KPNZtw/iaW8ct7PpVyhomg==", - "license": "MIT", - "dependencies": { - "@react-email/body": "0.1.0", - "@react-email/button": "0.2.0", - "@react-email/code-block": "0.1.0", - "@react-email/code-inline": "0.0.5", - "@react-email/column": "0.0.13", - "@react-email/container": "0.0.15", - "@react-email/font": "0.0.9", - "@react-email/head": "0.0.12", - "@react-email/heading": "0.0.15", - "@react-email/hr": "0.0.11", - "@react-email/html": "0.0.11", - "@react-email/img": "0.0.11", - "@react-email/link": "0.0.12", - "@react-email/markdown": "0.0.15", - "@react-email/preview": "0.0.13", - "@react-email/render": "1.3.2", - "@react-email/row": "0.0.12", - "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.2.2", - "@react-email/text": "0.1.5" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/container": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", - "integrity": "sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/font": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.9.tgz", - "integrity": "sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==", - "license": "MIT", - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/head": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.12.tgz", - "integrity": "sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/heading": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.15.tgz", - "integrity": "sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/hr": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.11.tgz", - "integrity": "sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/html": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.11.tgz", - "integrity": "sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/img": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.11.tgz", - "integrity": "sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/link": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.12.tgz", - "integrity": "sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/markdown": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.15.tgz", - "integrity": "sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==", - "license": "MIT", - "dependencies": { - "md-to-react-email": "^5.0.5" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/preview": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.13.tgz", - "integrity": "sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/preview-server": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.3.0.tgz", - "integrity": "sha512-cUaSrxezCzdg2hF6PzIxVrtagLdw3z3ovHeB3y2RDkmDZpp7EeIoNyJm22Ch2S0uAqTZNAgqu67aroLn3mFC1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "7.26.10", - "@babel/parser": "7.27.0", - "@babel/traverse": "7.27.0", - "@lottiefiles/dotlottie-react": "0.13.3", - "@radix-ui/colors": "3.0.0", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-dropdown-menu": "2.1.16", - "@radix-ui/react-popover": "1.1.15", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-tabs": "1.1.13", - "@radix-ui/react-toggle-group": "1.1.11", - "@radix-ui/react-tooltip": "1.2.8", - "@types/node": "22.14.1", - "@types/normalize-path": "3.0.2", - "@types/react": "19.0.10", - "@types/react-dom": "19.0.4", - "@types/webpack": "5.28.5", - "autoprefixer": "10.4.21", - "clsx": "2.1.1", - "esbuild": "0.25.10", - "framer-motion": "12.23.22", - "json5": "2.2.3", - "log-symbols": "4.1.0", - "module-punycode": "npm:punycode@2.3.1", - "next": "15.5.2", - "node-html-parser": "7.0.1", - "ora": "5.4.1", - "pretty-bytes": "6.1.1", - "prism-react-renderer": "2.4.1", - "react": "19.0.0", - "react-dom": "19.0.0", - "sharp": "0.34.4", - "socket.io-client": "4.8.1", - "sonner": "2.0.3", - "source-map-js": "1.2.1", - "spamc": "0.0.5", - "stacktrace-parser": "0.1.11", - "tailwind-merge": "3.2.0", - "tailwindcss": "3.4.0", - "use-debounce": "10.0.4", - "zod": "3.24.3" - } - }, - "node_modules/@react-email/preview-server/node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/env": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", - "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", - "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", - "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/@types/react": { - "version": "19.0.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", - "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@react-email/preview-server/node_modules/@types/react-dom": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", - "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/@react-email/preview-server/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@react-email/preview-server/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@react-email/preview-server/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/@react-email/preview-server/node_modules/next": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", - "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@next/env": "15.5.2", - "@swc/helpers": "0.5.15", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.2", - "@next/swc-darwin-x64": "15.5.2", - "@next/swc-linux-arm64-gnu": "15.5.2", - "@next/swc-linux-arm64-musl": "15.5.2", - "@next/swc-linux-x64-gnu": "15.5.2", - "@next/swc-linux-x64-musl": "15.5.2", - "@next/swc-win32-arm64-msvc": "15.5.2", - "@next/swc-win32-x64-msvc": "15.5.2", - "sharp": "^0.34.3" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@react-email/preview-server/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/@react-email/preview-server/node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/@react-email/preview-server/node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/@react-email/preview-server/node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "scheduler": "^0.25.0" - }, - "peerDependencies": { - "react": "^19.0.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-email/preview-server/node_modules/tailwind-merge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", - "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/@react-email/preview-server/node_modules/tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@react-email/preview-server/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-email/preview-server/node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@react-email/render": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.2.tgz", - "integrity": "sha512-oq8/BD/I/YspeuBjjdLJG6xaf9tsPYk+VWu8/mX9xWbRN0t0ExKSVm9sEBL6RsCpndQA2jbY2VgPEreIrzUgqw==", - "license": "MIT", - "dependencies": { - "html-to-text": "^9.0.5", - "prettier": "^3.5.3", - "react-promise-suspense": "^0.3.4" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/row": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.12.tgz", - "integrity": "sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/section": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.16.tgz", - "integrity": "sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/tailwind": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", - "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/text": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", - "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", - "license": "MIT" - }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" - }, - "node_modules/@schummar/icu-type-parser": { - "version": "1.21.5", - "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", - "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", - "license": "MIT" - }, - "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/@simplewebauthn/browser": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz", - "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==", - "license": "MIT" - }, - "node_modules/@simplewebauthn/server": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.2.tgz", - "integrity": "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA==", - "license": "MIT", - "dependencies": { - "@hexagon/base64": "^1.1.27", - "@levischuck/tiny-cbor": "^0.2.2", - "@peculiar/asn1-android": "^2.3.10", - "@peculiar/asn1-ecc": "^2.3.8", - "@peculiar/asn1-rsa": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "@peculiar/x509": "^1.13.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", - "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.1.0.tgz", - "integrity": "sha512-a36AtR7Q7XOhRPt6F/7HENmTWcB8kN7mDJcOFM/+FuKO6x88w8MQJfYCufMWh4fGyVkPjUh3Rrz/dnqFQdo6OQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.1.0.tgz", - "integrity": "sha512-Bnv0B3nSlfB2mPO0WgM49I/prl7+kamF042rrf3ezJ3Z4C7csPYvyYgZfXTGXwXfj1mAwDWjE/ybIf49PzFzvA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", - "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.13.0.tgz", - "integrity": "sha512-BI6ALLPOKnPOU1Cjkc+1TPhOlP3JXSR/UH14JmnaLq41t3ma+IjuXrKfhycVjr5IQ0XxRh2NnQo3olp+eCVrGg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/uuid": "^1.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", - "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz", - "integrity": "sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz", - "integrity": "sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz", - "integrity": "sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz", - "integrity": "sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz", - "integrity": "sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", - "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.1.1.tgz", - "integrity": "sha512-avAtk++s1e/1VODf+rg7c9R2pB5G9y8yaJaGY4lPZI2+UIqVyuSDMikWjeWfBVmFZ3O7NpDxBbUCyGhThVUKWQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.1.0", - "@smithy/chunked-blob-reader-native": "^4.1.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", - "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.1.1.tgz", - "integrity": "sha512-3ztT4pV0Moazs3JAYFdfKk11kYFDo4b/3R3+xVjIm6wY9YpJf+xfz+ocEnNKcWAdcmSMqi168i2EMaKmJHbJMA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", - "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", - "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.1.1.tgz", - "integrity": "sha512-MvWXKK743BuHjr/hnWuT6uStdKEaoqxHAQUvbKJPPZM5ZojTNFI5D+47BoQfBE5RgGlRRty05EbWA+NXDv+hIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", - "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.5.tgz", - "integrity": "sha512-DdOIpssQ5LFev7hV6GX9TMBW5ChTsQBxqgNW1ZGtJNSAi5ksd5klwPwwMY0ejejfEzwXXGqxgVO3cpaod4veiA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.13.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-middleware": "^4.1.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.3.1.tgz", - "integrity": "sha512-aH2bD1bzb6FB04XBhXA5mgedEZPKx3tD/qBuYCAKt5iieWvWO1Y2j++J9uLqOndXb9Pf/83Xka/YjSnMbcPchA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/service-error-classification": "^4.1.2", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/uuid": "^1.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", - "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", - "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", - "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", - "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", - "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", - "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", - "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-uri-escape": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", - "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.2.tgz", - "integrity": "sha512-Kqd8wyfmBWHZNppZSMfrQFpc3M9Y/kjyN8n8P4DqJJtuwgK1H914R471HTw7+RL+T7+kI1f1gOnL7Vb5z9+NgQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", - "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", - "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-uri-escape": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.5.tgz", - "integrity": "sha512-6J2hhuWu7EjnvLBIGltPCqzNswL1cW/AkaZx6i56qLsQ0ix17IAhmDD9aMmL+6CN9nCJODOXpBTCQS6iKAA7/g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.13.0", - "@smithy/middleware-endpoint": "^4.2.5", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", - "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", - "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", - "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", - "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", - "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", - "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", - "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.5.tgz", - "integrity": "sha512-FGBhlmFZVSRto816l6IwrmDcQ9pUYX6ikdR1mmAhdtSS1m77FgADukbQg7F7gurXfAvloxE/pgsrb7SGja6FQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.5.tgz", - "integrity": "sha512-Gwj8KLgJ/+MHYjVubJF0EELEh9/Ir7z7DFqyYlwgmp4J37KE+5vz6b3pWUnSt53tIe5FjDfVjDmHGYKjwIvW0Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.2.2", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.5", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", - "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", - "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", - "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.2.tgz", - "integrity": "sha512-NCgr1d0/EdeP6U5PSZ9Uv5SMR5XRRYoVr1kRVtKZxWL3tixEL3UatrPIMFZSKwHlCcp2zPLDvMubVDULRqeunA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.1.2", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.2.tgz", - "integrity": "sha512-Ka+FA2UCC/Q1dEqUanCdpqwxOFdf5Dg2VXtPtB1qxLcSGh5C1HdzklIt18xL504Wiy9nNUKwDMRTVCbKGoK69g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", - "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", - "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.1.1.tgz", - "integrity": "sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/uuid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.0.0.tgz", - "integrity": "sha512-OlA/yZHh0ekYFnbUkmYBDQPE6fGfdrvgz39ktp8Xf+FA6BfxLejPTMDOG0Nfk5/rDySAz1dRbFf24zaAFYVXlQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@so-ric/colorspace": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.5.tgz", - "integrity": "sha512-m2yI81kZ+2J2psTYtmBs3lfl/LM4WgLMCzkweMfQdbbyndcyOmFa6pblHjvjfblphPaGDzTDK+lmC/dUMSnv0A==", - "license": "MIT", - "dependencies": { - "color": "^5.0.2", - "text-hex": "1.0.x" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", - "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", - "license": "MIT", - "dependencies": { - "color-convert": "^3.0.1", - "color-string": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-convert": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", - "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", - "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/@so-ric/colorspace/node_modules/color-string": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", - "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/forms": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", - "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", - "license": "MIT", - "dependencies": { - "mini-svg-data-uri": "^1.2.3" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", - "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.0", - "lightningcss": "1.30.1", - "magic-string": "^0.30.19", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.14" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz", - "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.5.1" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.14", - "@tailwindcss/oxide-darwin-arm64": "4.1.14", - "@tailwindcss/oxide-darwin-x64": "4.1.14", - "@tailwindcss/oxide-freebsd-x64": "4.1.14", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", - "@tailwindcss/oxide-linux-x64-musl": "4.1.14", - "@tailwindcss/oxide-wasm32-wasi": "4.1.14", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz", - "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz", - "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.14.tgz", - "integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.14", - "@tailwindcss/oxide": "4.1.14", - "postcss": "^8.4.41", - "tailwindcss": "4.1.14" - } - }, - "node_modules/@tanstack/react-table": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", - "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", - "license": "MIT", - "dependencies": { - "@tanstack/table-core": "8.21.3" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/@tanstack/table-core": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", - "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@types/better-sqlite3": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", - "integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookie-parser": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", - "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/crypto-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", - "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/express-session": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jmespath": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.2.tgz", - "integrity": "sha512-pegh49FtNsC389Flyo9y8AfkVIZn9MMPE9yJrO9svhq6Fks2MwymULWjZqySuxmctd3ZH4/n7Mr98D+1Qo5vGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", - "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.14.0" - } - }, - "node_modules/@types/nodemailer": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.2.tgz", - "integrity": "sha512-Zo6uOA9157WRgBk/ZhMpTQ/iCWLMk7OIs/Q9jvHarMvrzUUP/MDdPHL2U1zpf57HrrWGv4nYQn5uIxna0xY3xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@aws-sdk/client-sesv2": "^3.839.0", - "@types/node": "*" - } - }, - "node_modules/@types/normalize-path": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/normalize-path/-/normalize-path-3.0.2.tgz", - "integrity": "sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/pg": { - "version": "8.15.5", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", - "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.1.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz", - "integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.1.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", - "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/@types/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/swagger-ui-express": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", - "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, - "node_modules/@types/webpack": { - "version": "5.28.5", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", - "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "tapable": "^2.2.0", - "webpack": "^5" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", - "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/type-utils": "8.46.0", - "@typescript-eslint/utils": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.46.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", - "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", - "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.0", - "@typescript-eslint/types": "^8.46.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", - "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", - "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", - "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0", - "@typescript-eslint/utils": "8.46.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", - "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", - "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.46.0", - "@typescript-eslint/tsconfig-utils": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", - "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", - "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arctic": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/arctic/-/arctic-3.7.0.tgz", - "integrity": "sha512-ZMQ+f6VazDgUJOd+qNV+H7GohNSYal1mVjm5kEaZfE2Ifb7Ss70w+Q7xpJC87qZDkMZIXYf0pTIYZA0OPasSbw==", - "license": "MIT", - "dependencies": { - "@oslojs/crypto": "1.0.1", - "@oslojs/encoding": "1.1.0", - "@oslojs/jwt": "0.2.0" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-move": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz", - "integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asn1js": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.6.tgz", - "integrity": "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==", - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "license": "MIT" - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/async-generator-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-generator-function/-/async-generator-function-1.0.0.tgz", - "integrity": "sha512-+NAXNqgCrB95ya4Sr66i1CL2hqLVckAk7xwRYWdcm39/ELQ6YNn1aw5r0bdQtqNZgQpEWzc5yc/igXc7aL5SLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", - "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", - "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/better-sqlite3": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.7.0.tgz", - "integrity": "sha512-mXpa5jnIKKHeoGzBrUJrc65cXFKcILGZpU3FXR0pradUEm9MA7UZz02qfEejaMcm9iXrSOCenwwYMJ/tZ1y5Ig==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/bowser": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", - "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.26.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", - "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001745", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", - "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/canvas-confetti": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz", - "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==", - "license": "ISC", - "funding": { - "type": "donate", - "url": "https://www.paypal.me/kirilvatev" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cmdk": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", - "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-id": "^1.1.0", - "@radix-ui/react-primitive": "^2.0.2" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "react-dom": "^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", - "license": "MIT", - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cookies": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", - "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "license": "BSD-2-Clause" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debounce": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", - "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "license": "MIT" - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-libc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", - "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/drizzle-kit": { - "version": "0.31.5", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.5.tgz", - "integrity": "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@drizzle-team/brocli": "^0.10.2", - "@esbuild-kit/esm-loader": "^2.5.5", - "esbuild": "^0.25.4", - "esbuild-register": "^3.5.0" - }, - "bin": { - "drizzle-kit": "bin.cjs" - } - }, - "node_modules/drizzle-orm": { - "version": "0.44.6", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.6.tgz", - "integrity": "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ==", - "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/client-rds-data": ">=3", - "@cloudflare/workers-types": ">=4", - "@electric-sql/pglite": ">=0.2.0", - "@libsql/client": ">=0.10.0", - "@libsql/client-wasm": ">=0.10.0", - "@neondatabase/serverless": ">=0.10.0", - "@op-engineering/op-sqlite": ">=2", - "@opentelemetry/api": "^1.4.1", - "@planetscale/database": ">=1.13", - "@prisma/client": "*", - "@tidbcloud/serverless": "*", - "@types/better-sqlite3": "*", - "@types/pg": "*", - "@types/sql.js": "*", - "@upstash/redis": ">=1.34.7", - "@vercel/postgres": ">=0.8.0", - "@xata.io/client": "*", - "better-sqlite3": ">=7", - "bun-types": "*", - "expo-sqlite": ">=14.0.0", - "gel": ">=2", - "knex": "*", - "kysely": "*", - "mysql2": ">=2", - "pg": ">=8", - "postgres": ">=3", - "sql.js": ">=1", - "sqlite3": ">=5" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-rds-data": { - "optional": true - }, - "@cloudflare/workers-types": { - "optional": true - }, - "@electric-sql/pglite": { - "optional": true - }, - "@libsql/client": { - "optional": true - }, - "@libsql/client-wasm": { - "optional": true - }, - "@neondatabase/serverless": { - "optional": true - }, - "@op-engineering/op-sqlite": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@prisma/client": { - "optional": true - }, - "@tidbcloud/serverless": { - "optional": true - }, - "@types/better-sqlite3": { - "optional": true - }, - "@types/pg": { - "optional": true - }, - "@types/sql.js": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/postgres": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "bun-types": { - "optional": true - }, - "expo-sqlite": { - "optional": true - }, - "gel": { - "optional": true - }, - "knex": { - "optional": true - }, - "kysely": { - "optional": true - }, - "mysql2": { - "optional": true + "name": "@fosrl/pangolin", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@fosrl/pangolin", + "version": "0.0.0", + "license": "SEE LICENSE IN LICENSE AND README.md", + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.3.4", + "@aws-sdk/client-s3": "3.837.0", + "@hookform/resolvers": "5.2.2", + "@node-rs/argon2": "^2.0.2", + "@oslojs/crypto": "1.0.1", + "@oslojs/encoding": "1.1.0", + "@radix-ui/react-avatar": "1.1.10", + "@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-icons": "1.3.2", + "@radix-ui/react-label": "2.1.7", + "@radix-ui/react-popover": "1.1.15", + "@radix-ui/react-progress": "^1.1.7", + "@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.7", + "@radix-ui/react-slot": "1.2.3", + "@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", + "@react-email/components": "0.5.6", + "@react-email/render": "^1.3.2", + "@react-email/tailwind": "1.2.2", + "@simplewebauthn/browser": "^13.2.2", + "@simplewebauthn/server": "^13.2.2", + "@tailwindcss/forms": "^0.5.10", + "@tanstack/react-table": "8.21.3", + "arctic": "^3.7.0", + "axios": "^1.12.2", + "better-sqlite3": "11.7.0", + "canvas-confetti": "1.9.3", + "class-variance-authority": "^0.7.1", + "clsx": "2.1.1", + "cmdk": "1.1.1", + "cookie": "^1.0.2", + "cookie-parser": "1.4.7", + "cookies": "^0.9.1", + "cors": "2.8.5", + "crypto-js": "^4.2.0", + "drizzle-orm": "0.44.6", + "eslint": "9.35.0", + "eslint-config-next": "15.5.4", + "express": "5.1.0", + "express-rate-limit": "8.1.0", + "glob": "11.0.3", + "helmet": "8.1.0", + "http-errors": "2.0.0", + "i": "^0.3.7", + "input-otp": "1.4.2", + "ioredis": "5.6.1", + "jmespath": "^0.16.0", + "js-yaml": "4.1.0", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.544.0", + "maxmind": "5.0.0", + "moment": "2.30.1", + "next": "15.5.4", + "next-intl": "^4.3.9", + "next-themes": "0.4.6", + "node-cache": "5.1.2", + "node-fetch": "3.3.2", + "nodemailer": "7.0.7", + "npm": "^11.6.1", + "oslo": "1.2.1", + "pg": "^8.16.2", + "posthog-node": "^5.9.3", + "qrcode.react": "4.2.0", + "react": "19.1.1", + "react-dom": "19.1.1", + "react-easy-sort": "^1.7.0", + "react-hook-form": "7.62.0", + "react-icons": "^5.5.0", + "rebuild": "0.1.2", + "reodotdev": "^1.0.0", + "resend": "^6.1.2", + "semver": "^7.7.2", + "stripe": "18.2.1", + "swagger-ui-express": "^5.0.1", + "tailwind-merge": "3.3.1", + "tw-animate-css": "^1.3.8", + "uuid": "^13.0.0", + "vaul": "1.1.2", + "winston": "3.17.0", + "winston-daily-rotate-file": "5.0.0", + "ws": "8.18.3", + "yargs": "18.0.0", + "zod": "3.25.76", + "zod-validation-error": "3.5.2" + }, + "devDependencies": { + "@dotenvx/dotenvx": "1.51.0", + "@esbuild-plugins/tsconfig-paths": "0.1.2", + "@react-email/preview-server": "4.3.0", + "@tailwindcss/postcss": "^4.1.14", + "@types/better-sqlite3": "7.6.12", + "@types/cookie-parser": "1.4.9", + "@types/cors": "2.8.19", + "@types/crypto-js": "^4.2.2", + "@types/express": "5.0.3", + "@types/express-session": "^1.18.2", + "@types/jmespath": "^0.15.2", + "@types/js-yaml": "4.0.9", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "24.7.0", + "@types/nodemailer": "7.0.2", + "@types/pg": "8.15.5", + "@types/react": "19.1.16", + "@types/react-dom": "19.1.9", + "@types/semver": "^7.7.1", + "@types/swagger-ui-express": "^4.1.8", + "@types/ws": "8.18.1", + "@types/yargs": "17.0.33", + "drizzle-kit": "0.31.5", + "esbuild": "0.25.10", + "esbuild-node-externals": "1.18.0", + "postcss": "^8", + "react-email": "4.3.0", + "tailwindcss": "^4.1.4", + "tsc-alias": "1.8.16", + "tsx": "4.20.6", + "typescript": "^5", + "typescript-eslint": "^8.46.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz", + "integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==", + "license": "MIT", + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.837.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.837.0.tgz", + "integrity": "sha512-sBjPPG30HIfNwpzWuajCDf7agb4YAxPFFpsp3kwgptJF8PEi0HzQg64bskquMzjqLC2tXsn5rKtDVpQOvs29MQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-node": "3.835.0", + "@aws-sdk/middleware-bucket-endpoint": "3.830.0", + "@aws-sdk/middleware-expect-continue": "3.821.0", + "@aws-sdk/middleware-flexible-checksums": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-location-constraint": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-s3": "3.835.0", + "@aws-sdk/middleware-ssec": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/signature-v4-multi-region": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.5", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-sesv2": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.901.0.tgz", + "integrity": "sha512-xCS2qZlvgbXKZbJW8XgU8OEAL7BJyVqJ5yODOQxa1TJFZ/+wEhik9XZtULjNnQqa29sJDpPltuSDG1aDG2OUxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-node": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/signature-v4-multi-region": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/client-sso": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.901.0.tgz", + "integrity": "sha512-sGyDjjkJ7ppaE+bAKL/Q5IvVCxtoyBIzN+7+hWTS/mUxWJ9EOq9238IqmVIIK6sYNIzEf9yhobfMARasPYVTNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/core": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.901.0.tgz", + "integrity": "sha512-brKAc3y64tdhyuEf+OPIUln86bRTqkLgb9xkd6kUdIeA5+qmp/N6amItQz+RN4k4O3kqkCPYnAd3LonTKluobw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.901.0.tgz", + "integrity": "sha512-5hAdVl3tBuARh3zX5MLJ1P/d+Kr5kXtDU3xm1pxUEF4xt2XkEEpwiX5fbkNkz2rbh3BCt2gOHsAbh6b3M7n+DA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.901.0.tgz", + "integrity": "sha512-Ggr7+0M6QZEsrqRkK7iyJLf4LkIAacAxHz9c4dm9hnDdU7vqrlJm6g73IxMJXWN1bIV7IxfpzB11DsRrB/oNjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.901.0.tgz", + "integrity": "sha512-zxadcDS0hNJgv8n4hFYJNOXyfjaNE1vvqIiF/JzZSQpSSYXzCd+WxXef5bQh+W3giDtRUmkvP5JLbamEFjZKyw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.901.0.tgz", + "integrity": "sha512-dPuFzMF7L1s/lQyT3wDxqLe82PyTH+5o1jdfseTEln64LJMl0ZMWaKX/C1UFNDxaTd35Cgt1bDbjjAWHMiKSFQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-ini": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.901.0.tgz", + "integrity": "sha512-/IWgmgM3Cl1wTdJA5HqKMAojxLkYchh5kDuphApxKhupLu6Pu0JBOHU8A5GGeFvOycyaVwosod6zDduINZxe+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.901.0.tgz", + "integrity": "sha512-SjmqZQHmqFSET7+6xcZgtH7yEyh5q53LN87GqwYlJZ6KJ5oNw11acUNEhUOL1xTSJEvaWqwTIkS2zqrzLcM9bw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.901.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/token-providers": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.901.0.tgz", + "integrity": "sha512-NYjy/6NLxH9m01+pfpB4ql8QgAorJcu8tw69kzHwUd/ql6wUDTbC7HcXqtKlIwWjzjgj2BKL7j6SyFapgCuafA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.901.0.tgz", + "integrity": "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-logger": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.901.0.tgz", + "integrity": "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.901.0.tgz", + "integrity": "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.901.0.tgz", + "integrity": "sha512-prgjVC3fDT2VIlmQPiw/cLee8r4frTam9GILRUVQyDdNtshNwV3MiaSCLzzQJjKJlLgnBLNUHJCSmvUVtg+3iA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.901.0.tgz", + "integrity": "sha512-Zby4F03fvD9xAgXGPywyk4bC1jCbnyubMEYChLYohD+x20ULQCf+AimF/Btn7YL+hBpzh1+RmqmvZcx+RgwgNQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/nested-clients": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.901.0.tgz", + "integrity": "sha512-feAAAMsVwctk2Tms40ONybvpfJPLCmSdI+G+OTrNpizkGLNl6ik2Ng2RzxY6UqOfN8abqKP/DOUj1qYDRDG8ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.901.0.tgz", + "integrity": "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.901.0.tgz", + "integrity": "sha512-2IWxbll/pRucp1WQkHi2W5E2SVPGBvk4Is923H7gpNksbVFws18ItjMM8ZpGm44cJEoy1zR5gjhLFklatpuoOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/token-providers": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.901.0.tgz", + "integrity": "sha512-pJEr1Ggbc/uVTDqp9IbNu9hdr0eQf3yZix3s4Nnyvmg4xmJSGAlbPC9LrNr5u3CDZoc8Z9CuLrvbP4MwYquNpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-endpoints": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.901.0.tgz", + "integrity": "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", + "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.901.0.tgz", + "integrity": "sha512-l59KQP5TY7vPVUfEURc7P5BJKuNg1RSsAKBQW7LHLECXjLqDUbo2SMLrexLBEoArSt6E8QOrIN0C8z/0Xk0jYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/xml-builder": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.901.0.tgz", + "integrity": "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-sesv2/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", + "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", + "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", + "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", + "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", + "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", + "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-ini": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", + "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", + "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.835.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/token-providers": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", + "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.830.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.830.0.tgz", + "integrity": "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", + "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.835.0.tgz", + "integrity": "sha512-9ezorQYlr5cQY28zWAReFhNKUTaXsi3TMvXIagMRrSeWtQ7R6TCYnt91xzHRCmFR2kp3zLI+dfoeH+wF3iCKUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", + "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.835.0.tgz", + "integrity": "sha512-oPebxpVf9smInHhevHh3APFZagGU+4RPwXEWv9YtYapFvsMq+8QXFvOfxfVZ/mwpe0JVG7EiJzL9/9Kobmts8Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", + "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", + "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@smithy/core": "^3.5.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", + "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.835.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.835.0.tgz", + "integrity": "sha512-rEtJH4dIwJYlXXe5rIH+uTCQmd2VIjuaoHlDY3Dr4nxF6po6U7vKsLfybIU2tgflGVqoqYQnXsfW/kj/Rh+/ow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", + "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", + "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", + "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.835.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", + "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@dotenvx/dotenvx": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.51.0.tgz", + "integrity": "sha512-CbMGzyOYSyFF7d4uaeYwO9gpSBzLTnMmSmTVpCZjvpJFV69qYbjYPpzNnCz1mb2wIvEhjWjRwQWuBzTO0jITww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "commander": "^11.1.0", + "dotenv": "^17.2.1", + "eciesjs": "^0.4.10", + "execa": "^5.1.1", + "fdir": "^6.2.0", + "ignore": "^5.3.0", + "object-treeify": "1.1.33", + "picomatch": "^4.0.2", + "which": "^4.0.0" + }, + "bin": { + "dotenvx": "src/cli/dotenvx.js" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@drizzle-team/brocli": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@ecies/ciphers": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.4.tgz", + "integrity": "sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==", + "dev": true, + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", + "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild-plugins/tsconfig-paths": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/tsconfig-paths/-/tsconfig-paths-0.1.2.tgz", + "integrity": "sha512-TusFR26Y+Ze+Zm+NdfqZTSG4XyrXKxIaAfYCL3jASEI/gHjSdoCujATjzNWaaXs6Sk6Bv2D7NLr4Jdz1gysy/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "debug": "^4.3.1", + "find-up": "^5.0.0", + "strip-json-comments": "^3.1.1" + }, + "peerDependencies": { + "esbuild": "*", + "typescript": "*" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.5.tgz", + "integrity": "sha512-1HTESOq1IUa23g1lFZEGIXsfZKZOwWmB9RROwGn+xariiQnd++wwTMvlRAbZ8wtXRHFUamJPxsKcxpSzeCvFWQ==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.2", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.3.tgz", + "integrity": "sha512-H/KfWSosaiDiOaW4nHe1Fn4Cgzm+oFQ8giTmB5RJzTBNSMmd+j2NVrvvZHAmlxJHcuOelzKBLjQ2EDcyH4NSWw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.5", + "@formatjs/icu-skeleton-parser": "1.8.15", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.15.tgz", + "integrity": "sha512-qNrKxWJmnWxin5U4A4Evy7C0rgRiNw3IqXu9OGuT31B8lDxBGl+OgT8kcq0ZVKK0gqA4l4SQB9x+SFAvLT5hcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.5", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", + "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@hexagon/base64": { + "version": "1.1.28", + "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz", + "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", + "license": "MIT" + }, + "node_modules/@hookform/resolvers": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", + "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", + "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", + "license": "MIT" + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@levischuck/tiny-cbor": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", + "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==", + "license": "MIT" + }, + "node_modules/@lottiefiles/dotlottie-react": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.13.3.tgz", + "integrity": "sha512-V4FfdYlqzjBUX7f0KV6vfQOOI0Cp+3XeG/ZqSDFSEVg5P7fpROpDv5/I9aTM8sOCESK1SWT96Fem+QVUnBV1wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lottiefiles/dotlottie-web": "0.42.0" + }, + "peerDependencies": { + "react": "^17 || ^18 || ^19" + } + }, + "node_modules/@lottiefiles/dotlottie-web": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.42.0.tgz", + "integrity": "sha512-Zr2LCaOAoPCsdAQgeLyCSiQ1+xrAJtRCyuEYDj0qR5heUwpc+Pxbb88JyTVumcXFfKOBMOMmrlsTScLz2mrvQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.4.tgz", + "integrity": "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.4.tgz", + "integrity": "sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==", + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.4.tgz", + "integrity": "sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.4.tgz", + "integrity": "sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.4.tgz", + "integrity": "sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.4.tgz", + "integrity": "sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz", + "integrity": "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz", + "integrity": "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.4.tgz", + "integrity": "sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.4.tgz", + "integrity": "sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@node-rs/argon2": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-2.0.2.tgz", + "integrity": "sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "2.0.2", + "@node-rs/argon2-android-arm64": "2.0.2", + "@node-rs/argon2-darwin-arm64": "2.0.2", + "@node-rs/argon2-darwin-x64": "2.0.2", + "@node-rs/argon2-freebsd-x64": "2.0.2", + "@node-rs/argon2-linux-arm-gnueabihf": "2.0.2", + "@node-rs/argon2-linux-arm64-gnu": "2.0.2", + "@node-rs/argon2-linux-arm64-musl": "2.0.2", + "@node-rs/argon2-linux-x64-gnu": "2.0.2", + "@node-rs/argon2-linux-x64-musl": "2.0.2", + "@node-rs/argon2-wasm32-wasi": "2.0.2", + "@node-rs/argon2-win32-arm64-msvc": "2.0.2", + "@node-rs/argon2-win32-ia32-msvc": "2.0.2", + "@node-rs/argon2-win32-x64-msvc": "2.0.2" + } + }, + "node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-2.0.2.tgz", + "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-android-arm64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-2.0.2.tgz", + "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-arm64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-2.0.2.tgz", + "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-x64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-2.0.2.tgz", + "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-freebsd-x64": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-2.0.2.tgz", + "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-2.0.2.tgz", + "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-2.0.2.tgz", + "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-2.0.2.tgz", + "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", + "integrity": "sha512-ZM3jrHuJ0dKOhvA80gKJqBpBRmTJTFSo2+xVZR+phQcbAKRlDMSZMFDiKbSTnctkfwNFtjgDdh5g1vaEV04AvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-2.0.2.tgz", + "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-2.0.2.tgz", + "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-2.0.2.tgz", + "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-2.0.2.tgz", + "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-2.0.2.tgz", + "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", + "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.9.0", + "@node-rs/bcrypt-android-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-x64": "1.9.0", + "@node-rs/bcrypt-freebsd-x64": "1.9.0", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-x64-musl": "1.9.0", + "@node-rs/bcrypt-wasm32-wasi": "1.9.0", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" + } + }, + "node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", + "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", + "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", + "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@oslojs/asn1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz", + "integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==", + "license": "MIT", + "dependencies": { + "@oslojs/binary": "1.0.0" + } + }, + "node_modules/@oslojs/binary": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz", + "integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==", + "license": "MIT" + }, + "node_modules/@oslojs/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==", + "license": "MIT", + "dependencies": { + "@oslojs/asn1": "1.0.0", + "@oslojs/binary": "1.0.0" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oslojs/jwt": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@oslojs/jwt/-/jwt-0.2.0.tgz", + "integrity": "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==", + "license": "MIT", + "dependencies": { + "@oslojs/encoding": "0.4.1" + } + }, + "node_modules/@oslojs/jwt/node_modules/@oslojs/encoding": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz", + "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==", + "license": "MIT" + }, + "node_modules/@peculiar/asn1-android": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.5.0.tgz", + "integrity": "sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.5.0.tgz", + "integrity": "sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "@peculiar/asn1-x509-attr": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.5.0.tgz", + "integrity": "sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.5.0.tgz", + "integrity": "sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.5.0.tgz", + "integrity": "sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-pkcs8": "^2.5.0", + "@peculiar/asn1-rsa": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.5.0.tgz", + "integrity": "sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.5.0.tgz", + "integrity": "sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-pfx": "^2.5.0", + "@peculiar/asn1-pkcs8": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "@peculiar/asn1-x509-attr": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.5.0.tgz", + "integrity": "sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.5.0.tgz", + "integrity": "sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==", + "license": "MIT", + "dependencies": { + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.5.0.tgz", + "integrity": "sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.5.0.tgz", + "integrity": "sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.0.tgz", + "integrity": "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-csr": "^2.5.0", + "@peculiar/asn1-ecc": "^2.5.0", + "@peculiar/asn1-pkcs9": "^2.5.0", + "@peculiar/asn1-rsa": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@posthog/core": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.2.2.tgz", + "integrity": "sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==", + "license": "MIT" + }, + "node_modules/@radix-ui/colors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", + "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", + "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", + "license": "MIT", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@react-email/body": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.1.0.tgz", + "integrity": "sha512-o1bcSAmDYNNHECbkeyceCVPGmVsYvT+O3sSO/Ct7apKUu3JphTi31hu+0Nwqr/pgV5QFqdoT5vdS3SW5DJFHgQ==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/button": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.0.tgz", + "integrity": "sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-block": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", + "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-inline": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.5.tgz", + "integrity": "sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/column": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.13.tgz", + "integrity": "sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/components": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.6.tgz", + "integrity": "sha512-3o9ellDaF3bBcVMWeos9HI0iUIT1zGygPRcn9WSfI5JREORiN6ViEJIvz5SKWEn1KPNZtw/iaW8ct7PpVyhomg==", + "license": "MIT", + "dependencies": { + "@react-email/body": "0.1.0", + "@react-email/button": "0.2.0", + "@react-email/code-block": "0.1.0", + "@react-email/code-inline": "0.0.5", + "@react-email/column": "0.0.13", + "@react-email/container": "0.0.15", + "@react-email/font": "0.0.9", + "@react-email/head": "0.0.12", + "@react-email/heading": "0.0.15", + "@react-email/hr": "0.0.11", + "@react-email/html": "0.0.11", + "@react-email/img": "0.0.11", + "@react-email/link": "0.0.12", + "@react-email/markdown": "0.0.15", + "@react-email/preview": "0.0.13", + "@react-email/render": "1.3.2", + "@react-email/row": "0.0.12", + "@react-email/section": "0.0.16", + "@react-email/tailwind": "1.2.2", + "@react-email/text": "0.1.5" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/container": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", + "integrity": "sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/font": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.9.tgz", + "integrity": "sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/head": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.12.tgz", + "integrity": "sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/heading": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.15.tgz", + "integrity": "sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/hr": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.11.tgz", + "integrity": "sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/html": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.11.tgz", + "integrity": "sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/img": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.11.tgz", + "integrity": "sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/link": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.12.tgz", + "integrity": "sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/markdown": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.15.tgz", + "integrity": "sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==", + "license": "MIT", + "dependencies": { + "md-to-react-email": "^5.0.5" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/preview": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.13.tgz", + "integrity": "sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/preview-server": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@react-email/preview-server/-/preview-server-4.3.0.tgz", + "integrity": "sha512-cUaSrxezCzdg2hF6PzIxVrtagLdw3z3ovHeB3y2RDkmDZpp7EeIoNyJm22Ch2S0uAqTZNAgqu67aroLn3mFC1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.26.10", + "@babel/parser": "7.27.0", + "@babel/traverse": "7.27.0", + "@lottiefiles/dotlottie-react": "0.13.3", + "@radix-ui/colors": "3.0.0", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-dropdown-menu": "2.1.16", + "@radix-ui/react-popover": "1.1.15", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-tabs": "1.1.13", + "@radix-ui/react-toggle-group": "1.1.11", + "@radix-ui/react-tooltip": "1.2.8", + "@types/node": "22.14.1", + "@types/normalize-path": "3.0.2", + "@types/react": "19.0.10", + "@types/react-dom": "19.0.4", + "@types/webpack": "5.28.5", + "autoprefixer": "10.4.21", + "clsx": "2.1.1", + "esbuild": "0.25.10", + "framer-motion": "12.23.22", + "json5": "2.2.3", + "log-symbols": "4.1.0", + "module-punycode": "npm:punycode@2.3.1", + "next": "15.5.2", + "node-html-parser": "7.0.1", + "ora": "5.4.1", + "pretty-bytes": "6.1.1", + "prism-react-renderer": "2.4.1", + "react": "19.0.0", + "react-dom": "19.0.0", + "sharp": "0.34.4", + "socket.io-client": "4.8.1", + "sonner": "2.0.3", + "source-map-js": "1.2.1", + "spamc": "0.0.5", + "stacktrace-parser": "0.1.11", + "tailwind-merge": "3.2.0", + "tailwindcss": "3.4.0", + "use-debounce": "10.0.4", + "zod": "3.24.3" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/env": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", + "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-arm64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", + "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-x64": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", + "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", + "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", + "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", + "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", + "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", + "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", + "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@react-email/preview-server/node_modules/@types/react-dom": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@react-email/preview-server/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@react-email/preview-server/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/@react-email/preview-server/node_modules/next": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", + "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "15.5.2", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.2", + "@next/swc-darwin-x64": "15.5.2", + "@next/swc-linux-arm64-gnu": "15.5.2", + "@next/swc-linux-arm64-musl": "15.5.2", + "@next/swc-linux-x64-gnu": "15.5.2", + "@next/swc-linux-x64-musl": "15.5.2", + "@next/swc-win32-arm64-msvc": "15.5.2", + "@next/swc-win32-x64-msvc": "15.5.2", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@react-email/preview-server/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@react-email/preview-server/node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/tailwind-merge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", + "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/@react-email/preview-server/node_modules/tailwindcss": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", + "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@react-email/preview-server/node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/@react-email/preview-server/node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/@react-email/preview-server/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-email/preview-server/node_modules/zod": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@react-email/render": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.3.2.tgz", + "integrity": "sha512-oq8/BD/I/YspeuBjjdLJG6xaf9tsPYk+VWu8/mX9xWbRN0t0ExKSVm9sEBL6RsCpndQA2jbY2VgPEreIrzUgqw==", + "license": "MIT", + "dependencies": { + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/row": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.12.tgz", + "integrity": "sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/section": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.16.tgz", + "integrity": "sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/tailwind": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", + "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/text": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", + "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.13.0.tgz", + "integrity": "sha512-2ih5qGw5SZJ+2fLZxP6Lr6Na2NTIgPRL/7Kmyuw0uIyBQnuhQ8fi8fzUTd38eIQmqp+GYLC00cI6WgtqHxBwmw==", + "license": "MIT" + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", + "license": "MIT" + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@simplewebauthn/browser": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz", + "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==", + "license": "MIT" + }, + "node_modules/@simplewebauthn/server": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.2.tgz", + "integrity": "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA==", + "license": "MIT", + "dependencies": { + "@hexagon/base64": "^1.1.27", + "@levischuck/tiny-cbor": "^0.2.2", + "@peculiar/asn1-android": "^2.3.10", + "@peculiar/asn1-ecc": "^2.3.8", + "@peculiar/asn1-rsa": "^2.3.8", + "@peculiar/asn1-schema": "^2.3.8", + "@peculiar/asn1-x509": "^2.3.8", + "@peculiar/x509": "^1.13.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", + "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.0.tgz", + "integrity": "sha512-HNbGWdyTfSM1nfrZKQjYTvD8k086+M8s1EYkBUdGC++lhxegUp2HgNf5RIt6oOGVvsC26hBCW/11tv8KbwLn/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", + "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.14.0.tgz", + "integrity": "sha512-XJ4z5FxvY/t0Dibms/+gLJrI5niRoY0BCmE02fwmPcRYFPI4KI876xaE79YGWIKnEslMbuQPsIEsoU/DXa0DoA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", + "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.0.tgz", + "integrity": "sha512-XE7CtKfyxYiNZ5vz7OvyTf1osrdbJfmUy+rbh+NLQmZumMGvY0mT0Cq1qKSfhrvLtRYzMsOBuRpi10dyI0EBPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.0.tgz", + "integrity": "sha512-U53p7fcrk27k8irLhOwUu+UYnBqsXNLKl1XevOpsxK3y1Lndk8R7CSiZV6FN3fYFuTPuJy5pP6qa/bjDzEkRvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.0.tgz", + "integrity": "sha512-uwx54t8W2Yo9Jr3nVF5cNnkAAnMCJ8Wrm+wDlQY6rY/IrEgZS3OqagtCu/9ceIcZFQ1zVW/zbN9dxb5esuojfA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.0.tgz", + "integrity": "sha512-yjM2L6QGmWgJjVu/IgYd6hMzwm/tf4VFX0lm8/SvGbGBwc+aFl3hOzvO/e9IJ2XI+22Tx1Zg3vRpFRs04SWFcg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.0.tgz", + "integrity": "sha512-C3jxz6GeRzNyGKhU7oV656ZbuHY93mrfkT12rmjDdZch142ykjn8do+VOkeRNjSGKw01p4g+hdalPYPhmMwk1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.0.tgz", + "integrity": "sha512-BG3KSmsx9A//KyIfw+sqNmWFr1YBUr+TwpxFT7yPqAk0yyDh7oSNgzfNH7pS6OC099EGx2ltOULvumCFe8bcgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.0.tgz", + "integrity": "sha512-MWmrRTPqVKpN8NmxmJPTeQuhewTt8Chf+waB38LXHZoA02+BeWYVQ9ViAwHjug8m7lQb1UWuGqp3JoGDOWvvuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", + "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.0.tgz", + "integrity": "sha512-8dELAuGv+UEjtzrpMeNBZc1sJhO8GxFVV/Yh21wE35oX4lOE697+lsMHBoUIFAUuYkTMIeu0EuJSEsH7/8Y+UQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", + "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.0.tgz", + "integrity": "sha512-LFEPniXGKRQArFmDQ3MgArXlClFJMsXDteuQQY8WG1/zzv6gVSo96+qpkuu1oJp4MZsKrwchY0cuAoPKzEbaNA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", + "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.0.tgz", + "integrity": "sha512-jFVjuQeV8TkxaRlcCNg0GFVgg98tscsmIrIwRFeC74TIUyLE3jmY9xgc1WXrPQYRjQNK3aRoaIk6fhFRGOIoGw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.14.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.0.tgz", + "integrity": "sha512-yaVBR0vQnOnzex45zZ8ZrPzUnX73eUC8kVFaAAbn04+6V7lPtxn56vZEBBAhgS/eqD6Zm86o6sJs6FuQVoX5qg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", + "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", + "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", + "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", + "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", + "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", + "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", + "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", + "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", + "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", + "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", + "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.0.tgz", + "integrity": "sha512-3BDx/aCCPf+kkinYf5QQhdQ9UAGihgOVqI3QO5xQfSaIWvUE4KYLtiGRWsNe1SR7ijXC0QEPqofVp5Sb0zC8xQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.14.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", + "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", + "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.2.0.tgz", + "integrity": "sha512-+erInz8WDv5KPe7xCsJCp+1WCjSbah9gWcmUXc9NqmhyPx59tf7jqFz+za1tRG1Y5KM1Cy1rWCcGypylFp4mvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.0.tgz", + "integrity": "sha512-U8q1WsSZFjXijlD7a4wsDQOvOwV+72iHSfq1q7VD+V75xP/pdtm0WIGuaFJ3gcADDOKj2MIBn4+zisi140HEnQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.2.0.tgz", + "integrity": "sha512-qzHp7ZDk1Ba4LDwQVCNp90xPGqSu7kmL7y5toBpccuhi3AH7dcVBIT/pUxYcInK4jOy6FikrcTGq5wxcka8UaQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.0.tgz", + "integrity": "sha512-FxUHS3WXgx3bTWR6yQHNHHkQHZm/XKIi/CchTnKvBulN6obWpcbzJ6lDToXn+Wp0QlVKd7uYAz2/CTw1j7m+Kg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.3.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", + "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", + "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", + "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.4.0.tgz", + "integrity": "sha512-vtO7ktbixEcrVzMRmpQDnw/Ehr9UWjBvSJ9fyAbadKkC4w5Cm/4lMO8cHz8Ysb8uflvQUNRcuux/oNHKPXkffg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.0.tgz", + "integrity": "sha512-0Z+nxUU4/4T+SL8BCNN4ztKdQjToNvUYmkF1kXO5T7Yz3Gafzh0HeIG6mrkN8Fz3gn9hSyxuAT+6h4vM+iQSBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", + "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.0", + "lightningcss": "1.30.1", + "magic-string": "^0.30.19", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz", + "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.5.1" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-x64": "4.1.14", + "@tailwindcss/oxide-freebsd-x64": "4.1.14", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-x64-musl": "4.1.14", + "@tailwindcss/oxide-wasm32-wasi": "4.1.14", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz", + "integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz", + "integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz", + "integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz", + "integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz", + "integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz", + "integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz", + "integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz", + "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz", + "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz", + "integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.5", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz", + "integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz", + "integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.14.tgz", + "integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.14", + "@tailwindcss/oxide": "4.1.14", + "postcss": "^8.4.41", + "tailwindcss": "4.1.14" + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.12", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", + "integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jmespath": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.2.tgz", + "integrity": "sha512-pegh49FtNsC389Flyo9y8AfkVIZn9MMPE9yJrO9svhq6Fks2MwymULWjZqySuxmctd3ZH4/n7Mr98D+1Qo5vGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", + "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.2.tgz", + "integrity": "sha512-Zo6uOA9157WRgBk/ZhMpTQ/iCWLMk7OIs/Q9jvHarMvrzUUP/MDdPHL2U1zpf57HrrWGv4nYQn5uIxna0xY3xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aws-sdk/client-sesv2": "^3.839.0", + "@types/node": "*" + } + }, + "node_modules/@types/normalize-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/normalize-path/-/normalize-path-3.0.2.tgz", + "integrity": "sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pg": { + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz", + "integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", + "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", + "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/swagger-ui-express": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", + "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@types/webpack": { + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", + "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", + "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/type-utils": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", + "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", + "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.46.0", + "@typescript-eslint/types": "^8.46.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", + "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", + "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", + "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", + "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.0", + "@typescript-eslint/tsconfig-utils": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", + "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", + "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arctic": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/arctic/-/arctic-3.7.0.tgz", + "integrity": "sha512-ZMQ+f6VazDgUJOd+qNV+H7GohNSYal1mVjm5kEaZfE2Ifb7Ss70w+Q7xpJC87qZDkMZIXYf0pTIYZA0OPasSbw==", + "license": "MIT", + "dependencies": { + "@oslojs/crypto": "1.0.1", + "@oslojs/encoding": "1.1.0", + "@oslojs/jwt": "0.2.0" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-move": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz", + "integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.6.tgz", + "integrity": "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==", + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.13.tgz", + "integrity": "sha512-7s16KR8io8nIBWQyCYhmFhd+ebIzb9VKTzki+wOJXHTxTnV6+mFGH3+Jwn1zoKaY9/H9T/0BcKCZnzXljPnpSQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/better-sqlite3": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.7.0.tgz", + "integrity": "sha512-mXpa5jnIKKHeoGzBrUJrc65cXFKcILGZpU3FXR0pradUEm9MA7UZz02qfEejaMcm9iXrSOCenwwYMJ/tZ1y5Ig==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bowser": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", + "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001748", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz", + "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-confetti": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz", + "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/color": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "license": "BSD-2-Clause" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debounce": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drizzle-kit": { + "version": "0.31.5", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.5.tgz", + "integrity": "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@drizzle-team/brocli": "^0.10.2", + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.25.4", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.44.6", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.6.tgz", + "integrity": "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/eciesjs": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.15.tgz", + "integrity": "sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.3", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.1", + "@noble/hashes": "^1.8.0" + }, + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.233", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.233.tgz", + "integrity": "sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/esbuild-node-externals": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/esbuild-node-externals/-/esbuild-node-externals-1.18.0.tgz", + "integrity": "sha512-suFVX3SzZlXrGIS9Yqx+ZaHL4w1p0e/j7dQbOM9zk8SfFpnAGnDplHUKXIf9kcPEAfZRL66JuYeVSVlsSEQ5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "esbuild": "0.12 - 0.25" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.4.tgz", + "integrity": "sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==", + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "15.5.4", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.1.0.tgz", + "integrity": "sha512-4nLnATuKupnmwqiJc27b4dCFmB/T60ExgmtDD7waf4LdrbJ8CPZzZRHYErDYNhoz+ql8fUdYwM/opf90PoPAQA==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.23.22", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", + "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.21", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "license": "Unlicense", + "optional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.11.0.tgz", + "integrity": "sha512-sNsqf7XKQ38IawiVGPOoAlqZo1DMrO7TU+ZcZwi7yLl7/7S0JwmoBMKz/IkUPhSoXM0Ng3vT0yB1iCe5XavDeQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "license": "MIT", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/input-otp": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", + "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/intl-messageformat": { + "version": "10.7.17", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.17.tgz", + "integrity": "sha512-0Ugaf65B2J76rb31drgNF1l6bGEDkbIiYc2Glx6jaZINHnwa5kDRGy8KXYuA+/8P4G0c9prAFhfVhQJJfzUuvQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.5", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.3", + "tslib": "^2.8.0" + } + }, + "node_modules/ioredis": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", + "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.544.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", + "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", + "integrity": "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/maxmind": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-5.0.0.tgz", + "integrity": "sha512-ndhnbeQWKuiBU17BJ6cybUnvcyvNXaK+1VM5n9/I7+TIqAYFLDvX1DSoVfE1hgvZfudvAU9Ts1CW5sxYq/M8dA==", + "license": "MIT", + "dependencies": { + "mmdb-lib": "3.0.1", + "tiny-lru": "11.3.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/md-to-react-email": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/md-to-react-email/-/md-to-react-email-5.0.5.tgz", + "integrity": "sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==", + "license": "MIT", + "dependencies": { + "marked": "7.0.4" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", + "optional": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memfs-browser": { + "version": "3.5.10302", + "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", + "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", + "license": "Unlicense", + "optional": true, + "dependencies": { + "memfs": "3.5.3" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/mmdb-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.1.tgz", + "integrity": "sha512-dyAyMR+cRykZd1mw5altC9f4vKpCsuywPwo8l/L5fKqDay2zmqT0mF/BvUoXnQiqGn+nceO914rkPKJoyFnGxA==", + "license": "MIT", + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/module-punycode": { + "name": "punycode", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/motion-dom": { + "version": "12.23.21", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", + "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mylas": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", + "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz", + "integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.4", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.4", + "@next/swc-darwin-x64": "15.5.4", + "@next/swc-linux-arm64-gnu": "15.5.4", + "@next/swc-linux-arm64-musl": "15.5.4", + "@next/swc-linux-x64-gnu": "15.5.4", + "@next/swc-linux-x64-musl": "15.5.4", + "@next/swc-win32-arm64-msvc": "15.5.4", + "@next/swc-win32-x64-msvc": "15.5.4", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-intl": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.11.tgz", + "integrity": "sha512-kyjeGUuLBU1DqDVAzhgoltYxQ8esVqqqkq2BRKPFxTwHPT9r5P5ZHePu3esjc5B3dVeVC/yFf8ebEnaYo68q1g==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/intl-localematcher": "^0.5.4", + "negotiator": "^1.0.0", + "use-intl": "^4.3.11" + }, + "peerDependencies": { + "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-abi": { + "version": "3.78.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.78.0.tgz", + "integrity": "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "license": "MIT", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-html-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.0.1.tgz", + "integrity": "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.7.tgz", + "integrity": "sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", + "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which" + ], + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^9.1.5", + "@npmcli/config": "^10.4.1", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.1", + "@npmcli/promise-spawn": "^8.0.3", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^10.0.0", + "@sigstore/tuf": "^4.0.0", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^20.0.1", + "chalk": "^5.6.2", + "ci-info": "^4.3.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^11.0.3", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^9.0.0", + "ini": "^5.0.0", + "init-package-json": "^8.2.2", + "is-cidr": "^6.0.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^10.0.2", + "libnpmdiff": "^8.0.8", + "libnpmexec": "^10.1.7", + "libnpmfund": "^7.0.8", + "libnpmorg": "^8.0.1", + "libnpmpack": "^9.0.8", + "libnpmpublish": "^11.1.1", + "libnpmsearch": "^9.0.1", + "libnpmteam": "^8.0.2", + "libnpmversion": "^8.0.2", + "make-fetch-happen": "^15.0.2", + "minimatch": "^10.0.3", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.4.2", + "nopt": "^8.1.0", + "normalize-package-data": "^8.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.2", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-profile": "^12.0.0", + "npm-registry-fetch": "^19.0.0", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^21.0.3", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^10.2.2", + "tar": "^7.5.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^2.0.2", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.2", + "which": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "9.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^10.0.0", + "bin-links": "^5.0.0", + "cacache": "^20.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^9.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "10.4.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.1.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^7.0.0", + "glob": "^11.0.3", + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^20.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^11.0.3", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "4.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.5.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "4.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "4.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "20.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^11.0.3", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.6.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "8.0.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "11.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "8.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^7.0.0", + "npm-package-arg": "^13.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.7.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.2" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "10.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "6.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "4.1.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "10.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "8.0.8", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.1.5", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^3.0.0", + "diff": "^8.0.2", + "minimatch": "^10.0.3", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", + "tar": "^7.5.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "10.1.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.1.5", + "@npmcli/package-json": "^7.0.0", + "@npmcli/run-script": "^10.0.0", + "ci-info": "^4.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "read": "^4.0.0", + "semver": "^7.3.7", + "signal-exit": "^4.1.0", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "7.0.8", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.1.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "8.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^19.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "9.0.8", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.1.5", + "@npmcli/run-script": "^10.0.0", + "npm-package-arg": "^13.0.0", + "pacote": "^21.0.2" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "11.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^7.0.0", + "ci-info": "^4.0.0", + "npm-package-arg": "^13.0.0", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^4.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^19.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^19.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "@npmcli/run-script": "^10.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "11.2.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "15.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "10.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.4.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "8.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^9.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.2", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "13.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "10.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^19.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "19.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "21.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^4.0.0", + "ssri": "^12.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "2.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "4.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.0.0", + "@sigstore/tuf": "^4.0.0", + "@sigstore/verify": "^3.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.22", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "10.2.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "7.5.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.0.0", + "debug": "^4.4.1", + "make-fetch-happen": "^15.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nypm": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", + "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^2.0.0", + "tinyexec": "^0.3.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi3-ts": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "license": "MIT", + "dependencies": { + "yaml": "^2.8.0" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/oslo": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.1.tgz", + "integrity": "sha512-HfIhB5ruTdQv0XX2XlncWQiJ5SIHZ7NHZhVyHth0CSZ/xzge00etRyYy/3wp/Dsu+PkxMC+6+B2lS/GcKoewkA==", + "deprecated": "Package is no longer supported. Please see https://oslojs.dev for the successor project.", + "license": "MIT", + "dependencies": { + "@node-rs/argon2": "1.7.0", + "@node-rs/bcrypt": "1.9.0" + } + }, + "node_modules/oslo/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/oslo/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", + "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.0", + "@node-rs/argon2-android-arm64": "1.7.0", + "@node-rs/argon2-darwin-arm64": "1.7.0", + "@node-rs/argon2-darwin-x64": "1.7.0", + "@node-rs/argon2-freebsd-x64": "1.7.0", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", + "@node-rs/argon2-linux-arm64-gnu": "1.7.0", + "@node-rs/argon2-linux-arm64-musl": "1.7.0", + "@node-rs/argon2-linux-x64-gnu": "1.7.0", + "@node-rs/argon2-linux-x64-musl": "1.7.0", + "@node-rs/argon2-wasm32-wasi": "1.7.0", + "@node-rs/argon2-win32-arm64-msvc": "1.7.0", + "@node-rs/argon2-win32-ia32-msvc": "1.7.0", + "@node-rs/argon2-win32-x64-msvc": "1.7.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", + "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", + "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", + "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", + "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", + "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "license": "MIT", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/posthog-node": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.3.tgz", + "integrity": "sha512-YA32jx4MfL5uLiVU4ynNoSzRqAigG11pkbwEI3UlqyWJpWnp8Nvpzh59b8lpmm2+5Y+kJ99sKyWLGnNZ7K1ryA==", + "license": "MIT", + "dependencies": { + "@posthog/core": "1.2.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prism-react-renderer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", + "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/qrcode.react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", + "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/react-easy-sort": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-1.7.0.tgz", + "integrity": "sha512-82I63kXdawFhhlFrWPrI74DL48v2LKs7e7PLf5le2E/eIR9+XyCEdL4Pyjbru8XjvtQ60mPLb6oextc4PPR8Lg==", + "license": "MIT", + "dependencies": { + "array-move": "^3.0.1", + "tslib": "2.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, + "node_modules/react-easy-sort/node_modules/tslib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", + "license": "0BSD" + }, + "node_modules/react-email": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.3.0.tgz", + "integrity": "sha512-XFHCSfhdlO7k5q2TYGwC0HsVh5Yn13YaOdahuJEUEOfOJKHEpSP4PKg7R/RiKFoK9cDvzunhY+58pXxz0vE2zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/traverse": "^7.27.0", + "chokidar": "^4.0.3", + "commander": "^13.0.0", + "debounce": "^2.0.0", + "esbuild": "^0.25.0", + "glob": "^11.0.0", + "jiti": "2.4.2", + "log-symbols": "^7.0.0", + "mime-types": "^3.0.0", + "normalize-path": "^3.0.0", + "nypm": "0.6.0", + "ora": "^8.0.0", + "prompts": "2.4.2", + "socket.io": "^4.8.1", + "tsconfig-paths": "4.2.0" + }, + "bin": { + "email": "dist/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/react-email/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-email/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-email/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/react-email/node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-email/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/react-hook-form": { + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-promise-suspense": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", + "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "license": "MIT" + }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rebuild": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/rebuild/-/rebuild-0.1.2.tgz", + "integrity": "sha512-EtDZ5IapND57htCrOOcfH7MzXCQKivzSZUIZIuc8H0xDHfmi9HDBZIyjT7Neh5GcUoxQ6hfsXluC+UrYLgGbZg==", + "dependencies": { + "optimist": "0.3.x" + }, + "bin": { + "rebuild": "cli.js" + }, + "engines": { + "node": ">=0.8.8" + } }, - "pg": { - "optional": true + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "postgres": { - "optional": true + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } }, - "prisma": { - "optional": true + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" }, - "sql.js": { - "optional": true + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/eciesjs": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.15.tgz", - "integrity": "sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ecies/ciphers": "^0.2.3", - "@noble/ciphers": "^1.3.0", - "@noble/curves": "^1.9.1", - "@noble/hashes": "^1.8.0" - }, - "engines": { - "bun": ">=1", - "deno": ">=2", - "node": ">=16" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.227", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", - "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" - } - }, - "node_modules/esbuild-node-externals": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/esbuild-node-externals/-/esbuild-node-externals-1.18.0.tgz", - "integrity": "sha512-suFVX3SzZlXrGIS9Yqx+ZaHL4w1p0e/j7dQbOM9zk8SfFpnAGnDplHUKXIf9kcPEAfZRL66JuYeVSVlsSEQ5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "esbuild": "0.12 - 0.25" - } - }, - "node_modules/esbuild-register": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", - "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "esbuild": ">=0.12 <1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", - "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.35.0", - "@eslint/plugin-kit": "^0.3.5", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-next": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.4.tgz", - "integrity": "sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==", - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "15.5.4", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "license": "ISC", - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "license": "MIT", - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "license": "MIT", - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.1.0.tgz", - "integrity": "sha512-4nLnATuKupnmwqiJc27b4dCFmB/T60ExgmtDD7waf4LdrbJ8CPZzZRHYErDYNhoz+ql8fUdYwM/opf90PoPAQA==", - "license": "MIT", - "dependencies": { - "ip-address": "10.0.1" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-stream-rotator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", - "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", - "license": "MIT", - "dependencies": { - "moment": "^2.29.1" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "license": "ISC" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/framer-motion": { - "version": "12.23.22", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", - "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "motion-dom": "^12.23.21", - "motion-utils": "^12.23.6", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.0.tgz", - "integrity": "sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.1.tgz", - "integrity": "sha512-fk1ZVEeOX9hVZ6QzoBNEC55+Ucqg4sTVwrVuigZhuRPESVFpMyXnd3sbXvPOwp7Y9riVyANiqhEuRF0G1aVSeQ==", - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "async-generator-function": "^1.0.0", - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "license": "MIT" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/helmet": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", - "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", - "license": "MIT", - "dependencies": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", - "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/i": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", - "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/input-otp": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", - "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.16", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", - "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.2", - "tslib": "^2.8.0" - } - }, - "node_modules/ioredis": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", - "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz", - "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==", - "devOptional": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "license": "MIT", - "dependencies": { - "tsscmp": "1.0.6" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/logform": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "0.544.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", - "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.19", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", - "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/marked": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", - "integrity": "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 16" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/maxmind": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-5.0.0.tgz", - "integrity": "sha512-ndhnbeQWKuiBU17BJ6cybUnvcyvNXaK+1VM5n9/I7+TIqAYFLDvX1DSoVfE1hgvZfudvAU9Ts1CW5sxYq/M8dA==", - "license": "MIT", - "dependencies": { - "mmdb-lib": "3.0.1", - "tiny-lru": "11.3.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/md-to-react-email": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/md-to-react-email/-/md-to-react-email-5.0.5.tgz", - "integrity": "sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==", - "license": "MIT", - "dependencies": { - "marked": "7.0.4" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "license": "MIT", - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/mmdb-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.1.tgz", - "integrity": "sha512-dyAyMR+cRykZd1mw5altC9f4vKpCsuywPwo8l/L5fKqDay2zmqT0mF/BvUoXnQiqGn+nceO914rkPKJoyFnGxA==", - "license": "MIT", - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/module-punycode": { - "name": "punycode", - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/motion-dom": { - "version": "12.23.21", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", - "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "motion-utils": "^12.23.6" - } - }, - "node_modules/motion-utils": { - "version": "12.23.6", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", - "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mylas": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", - "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/raouldeheer" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/napi-postinstall": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", - "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/next": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz", - "integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==", - "license": "MIT", - "dependencies": { - "@next/env": "15.5.4", - "@swc/helpers": "0.5.15", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.4", - "@next/swc-darwin-x64": "15.5.4", - "@next/swc-linux-arm64-gnu": "15.5.4", - "@next/swc-linux-arm64-musl": "15.5.4", - "@next/swc-linux-x64-gnu": "15.5.4", - "@next/swc-linux-x64-musl": "15.5.4", - "@next/swc-win32-arm64-msvc": "15.5.4", - "@next/swc-win32-x64-msvc": "15.5.4", - "sharp": "^0.34.3" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-intl": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.9.tgz", - "integrity": "sha512-4oSROHlgy8a5Qr2vH69wxo9F6K0uc6nZM2GNzqSe6ET79DEzOmBeSijCRzD5txcI4i+XTGytu4cxFsDXLKEDpQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], - "license": "MIT", - "dependencies": { - "@formatjs/intl-localematcher": "^0.5.4", - "negotiator": "^1.0.0", - "use-intl": "^4.3.9" - }, - "peerDependencies": { - "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-abi": { - "version": "3.77.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", - "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "license": "MIT", - "dependencies": { - "clone": "2.x" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-html-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.0.1.tgz", - "integrity": "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.7.tgz", - "integrity": "sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm": { - "version": "11.6.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", - "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/redact", - "@npmcli/run-script", - "@sigstore/tuf", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "normalize-package-data", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "spdx-expression-parse", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which" - ], - "license": "Artistic-2.0", - "workspaces": [ - "docs", - "smoke-tests", - "mock-globals", - "mock-registry", - "workspaces/*" - ], - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.5", - "@npmcli/config": "^10.4.1", - "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/package-json": "^7.0.1", - "@npmcli/promise-spawn": "^8.0.3", - "@npmcli/redact": "^3.2.2", - "@npmcli/run-script": "^10.0.0", - "@sigstore/tuf": "^4.0.0", - "abbrev": "^3.0.1", - "archy": "~1.0.0", - "cacache": "^20.0.1", - "chalk": "^5.6.2", - "ci-info": "^4.3.0", - "cli-columns": "^4.0.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.3", - "glob": "^11.0.3", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^9.0.0", - "ini": "^5.0.0", - "init-package-json": "^8.2.2", - "is-cidr": "^6.0.0", - "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.2", - "libnpmdiff": "^8.0.8", - "libnpmexec": "^10.1.7", - "libnpmfund": "^7.0.8", - "libnpmorg": "^8.0.1", - "libnpmpack": "^9.0.8", - "libnpmpublish": "^11.1.1", - "libnpmsearch": "^9.0.1", - "libnpmteam": "^8.0.2", - "libnpmversion": "^8.0.2", - "make-fetch-happen": "^15.0.2", - "minimatch": "^10.0.3", - "minipass": "^7.1.1", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^11.4.2", - "nopt": "^8.1.0", - "normalize-package-data": "^8.0.0", - "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.2", - "npm-package-arg": "^13.0.0", - "npm-pick-manifest": "^11.0.1", - "npm-profile": "^12.0.0", - "npm-registry-fetch": "^19.0.0", - "npm-user-validate": "^3.0.0", - "p-map": "^7.0.3", - "pacote": "^21.0.3", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "qrcode-terminal": "^0.12.0", - "read": "^4.1.0", - "semver": "^7.7.2", - "spdx-expression-parse": "^4.0.0", - "ssri": "^12.0.0", - "supports-color": "^10.2.2", - "tar": "^7.5.1", - "text-table": "~0.2.0", - "tiny-relative-date": "^2.0.2", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^6.0.2", - "which": "^5.0.0" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/agent": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^11.2.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^4.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/metavuln-calculator": "^9.0.2", - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/query": "^4.0.0", - "@npmcli/redact": "^3.0.0", - "@npmcli/run-script": "^10.0.0", - "bin-links": "^5.0.0", - "cacache": "^20.0.1", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^9.0.0", - "json-stringify-nice": "^1.1.4", - "lru-cache": "^11.2.1", - "minimatch": "^10.0.3", - "nopt": "^8.0.0", - "npm-install-checks": "^7.1.0", - "npm-package-arg": "^13.0.0", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "pacote": "^21.0.2", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "proggy": "^3.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^3.0.1", - "semver": "^7.3.7", - "ssri": "^12.0.0", - "treeverse": "^3.0.0", - "walk-up-path": "^4.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "ini": "^5.0.0", - "nopt": "^8.1.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "7.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^8.0.0", - "ini": "^5.0.0", - "lru-cache": "^11.2.1", - "npm-pick-manifest": "^11.0.1", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/package-json": "^7.0.0", - "glob": "^11.0.3", - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^20.0.0", - "json-parse-even-better-errors": "^4.0.0", - "pacote": "^21.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "glob": "^11.0.3", - "hosted-git-info": "^9.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.5.3", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "which": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "4.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/redact": { - "version": "3.2.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "10.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "node-gyp": "^11.0.0", - "proc-log": "^5.0.0", - "which": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "4.0.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/core": { - "version": "3.0.0", - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.5.0", - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@sigstore/sign": { - "version": "4.0.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "4.0.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/verify": { - "version": "3.0.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@tufjs/models": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/agent-base": { - "version": "7.1.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.3", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^7.0.0", - "npm-normalize-package-bin": "^4.0.0", - "proc-log": "^5.0.0", - "read-cmd-shim": "^5.0.0", - "write-file-atomic": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/cacache": { - "version": "20.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^11.0.3", - "lru-cache": "^11.1.0", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/chalk": { - "version": "5.6.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "3.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/ci-info": { - "version": "4.3.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "7.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.6", - "inBundle": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.4.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/diff": { - "version": "8.0.2", - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.2", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/npm/node_modules/foreground-child": { - "version": "3.3.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "11.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "9.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.2.0", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "7.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.6", - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "8.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/ini": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "8.2.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "npm-package-arg": "^13.0.0", - "promzard": "^2.0.0", - "read": "^4.0.0", - "semver": "^7.7.2", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^6.0.2" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/ip-address": { - "version": "10.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "6.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/isexe": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/npm/node_modules/jackspeak": { - "version": "4.1.1", - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "10.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.8", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/installed-package-contents": "^3.0.0", - "binary-extensions": "^3.0.0", - "diff": "^8.0.2", - "minimatch": "^10.0.3", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "tar": "^7.5.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.7", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/package-json": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "read": "^4.0.0", - "semver": "^7.3.7", - "signal-exit": "^4.1.0", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.8", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "8.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.8", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/run-script": "^10.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.1.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.7", - "sigstore": "^4.0.0", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "9.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "8.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "8.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "11.2.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "15.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "10.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "4.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/negotiator": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "11.4.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^14.0.3", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "tar": "^7.4.3", - "tinyglobby": "^0.2.12", - "which": "^5.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { - "version": "19.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "tar": "^7.4.3", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "10.4.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/jackspeak": { - "version": "3.4.3", - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/lru-cache": { - "version": "10.4.3", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "14.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "9.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/path-scurry": { - "version": "1.11.1", - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "8.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "8.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^9.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.2", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "13.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^9.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^8.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "11.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^13.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "12.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "19.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^3.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^15.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minizlib": "^3.0.1", - "npm-package-arg": "^13.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "3.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "7.0.3", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/package-json-from-dist": { - "version": "1.0.1", - "inBundle": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/npm/node_modules/pacote": { - "version": "21.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^10.0.0", - "cacache": "^20.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^13.0.0", - "npm-packlist": "^10.0.1", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^4.0.0", - "ssri": "^12.0.0", - "tar": "^7.4.3" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/path-scurry": { - "version": "2.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/proc-log": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/proggy": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/read": { - "version": "4.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^2.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/npm/node_modules/semver": { - "version": "7.7.2", - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "4.1.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/sigstore": { - "version": "4.0.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0", - "@sigstore/sign": "^4.0.0", - "@sigstore/tuf": "^4.0.0", - "@sigstore/verify": "^3.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.8.7", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.5", - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.5.0", - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.22", - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/ssri": { - "version": "12.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "10.2.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "7.5.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.15", - "inBundle": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/tuf-js": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "4.0.0", - "debug": "^4.4.1", - "make-fetch-happen": "^15.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/which": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nypm": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", - "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "pathe": "^2.0.3", - "pkg-types": "^2.0.0", - "tinyexec": "^0.3.2" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": "^14.16.0 || >=16.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-treeify": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", - "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi3-ts": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", - "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", - "license": "MIT", - "dependencies": { - "yaml": "^2.8.0" - } - }, - "node_modules/optimist": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", - "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", - "license": "MIT/X11", - "dependencies": { - "wordwrap": "~0.0.2" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/oslo": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.1.tgz", - "integrity": "sha512-HfIhB5ruTdQv0XX2XlncWQiJ5SIHZ7NHZhVyHth0CSZ/xzge00etRyYy/3wp/Dsu+PkxMC+6+B2lS/GcKoewkA==", - "deprecated": "Package is no longer supported. Please see https://oslojs.dev for the successor project.", - "license": "MIT", - "dependencies": { - "@node-rs/argon2": "1.7.0", - "@node-rs/bcrypt": "1.9.0" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", - "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@node-rs/argon2-android-arm-eabi": "1.7.0", - "@node-rs/argon2-android-arm64": "1.7.0", - "@node-rs/argon2-darwin-arm64": "1.7.0", - "@node-rs/argon2-darwin-x64": "1.7.0", - "@node-rs/argon2-freebsd-x64": "1.7.0", - "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", - "@node-rs/argon2-linux-arm64-gnu": "1.7.0", - "@node-rs/argon2-linux-arm64-musl": "1.7.0", - "@node-rs/argon2-linux-x64-gnu": "1.7.0", - "@node-rs/argon2-linux-x64-musl": "1.7.0", - "@node-rs/argon2-wasm32-wasi": "1.7.0", - "@node-rs/argon2-win32-arm64-msvc": "1.7.0", - "@node-rs/argon2-win32-ia32-msvc": "1.7.0", - "@node-rs/argon2-win32-x64-msvc": "1.7.0" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parseley": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", - "license": "MIT", - "dependencies": { - "leac": "^0.6.0", - "peberminta": "^0.9.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/peberminta": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/plimit-lit": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", - "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "queue-lit": "^1.5.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/posthog-node": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.3.tgz", - "integrity": "sha512-YA32jx4MfL5uLiVU4ynNoSzRqAigG11pkbwEI3UlqyWJpWnp8Nvpzh59b8lpmm2+5Y+kJ99sKyWLGnNZ7K1ryA==", - "license": "MIT", - "dependencies": { - "@posthog/core": "1.2.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-bytes": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", - "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prism-react-renderer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", - "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - } - }, - "node_modules/pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/qrcode.react": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", - "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", - "license": "ISC", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-lit": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", - "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.1" - } - }, - "node_modules/react-easy-sort": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-1.7.0.tgz", - "integrity": "sha512-82I63kXdawFhhlFrWPrI74DL48v2LKs7e7PLf5le2E/eIR9+XyCEdL4Pyjbru8XjvtQ60mPLb6oextc4PPR8Lg==", - "license": "MIT", - "dependencies": { - "array-move": "^3.0.1", - "tslib": "2.0.1" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "react": ">=16.4.0", - "react-dom": ">=16.4.0" - } - }, - "node_modules/react-easy-sort/node_modules/tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "license": "0BSD" - }, - "node_modules/react-email": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.3.0.tgz", - "integrity": "sha512-XFHCSfhdlO7k5q2TYGwC0HsVh5Yn13YaOdahuJEUEOfOJKHEpSP4PKg7R/RiKFoK9cDvzunhY+58pXxz0vE2zA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/traverse": "^7.27.0", - "chokidar": "^4.0.3", - "commander": "^13.0.0", - "debounce": "^2.0.0", - "esbuild": "^0.25.0", - "glob": "^11.0.0", - "jiti": "2.4.2", - "log-symbols": "^7.0.0", - "mime-types": "^3.0.0", - "normalize-path": "^3.0.0", - "nypm": "0.6.0", - "ora": "^8.0.0", - "prompts": "2.4.2", - "socket.io": "^4.8.1", - "tsconfig-paths": "4.2.0" - }, - "bin": { - "email": "dist/index.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/react-email/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-email/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/react-email/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "dev": true, - "license": "MIT" - }, - "node_modules/react-email/node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/react-email/node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/react-email/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/react-hook-form": { - "version": "7.62.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", - "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18 || ^19" - } - }, - "node_modules/react-icons": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", - "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/react-promise-suspense": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", - "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^2.0.1" - } - }, - "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", - "license": "MIT" - }, - "node_modules/react-remove-scroll": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", - "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/rebuild": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/rebuild/-/rebuild-0.1.2.tgz", - "integrity": "sha512-EtDZ5IapND57htCrOOcfH7MzXCQKivzSZUIZIuc8H0xDHfmi9HDBZIyjT7Neh5GcUoxQ6hfsXluC+UrYLgGbZg==", - "dependencies": { - "optimist": "0.3.x" - }, - "bin": { - "rebuild": "cli.js" - }, - "engines": { - "node": ">=0.8.8" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "license": "MIT", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/reodotdev": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/reodotdev/-/reodotdev-1.0.0.tgz", - "integrity": "sha512-wXe1vJucZjrhQL0SxOL9EvmJrtbMCIEGMdZX5lj/57n2T3UhBHZsAcM5TQASJ0T6ZBbrETRnMhH33bsbJeRO6Q==", - "license": "MIT" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resend": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/resend/-/resend-6.1.2.tgz", - "integrity": "sha512-C9Q+YkRe57P8MQlkHG3yatSR/B6sqBGA06Ri2DveJfkz9Vm16182FC/iHB0K6IAfmqZ4yRrFebFw1EPuktLtSg==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@react-email/render": "*" - }, - "peerDependenciesMeta": { - "@react-email/render": { - "optional": true - } - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/selderee": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", - "license": "MIT", - "dependencies": { - "parseley": "^0.12.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", - "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.0", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.4", - "@img/sharp-darwin-x64": "0.34.4", - "@img/sharp-libvips-darwin-arm64": "1.2.3", - "@img/sharp-libvips-darwin-x64": "1.2.3", - "@img/sharp-libvips-linux-arm": "1.2.3", - "@img/sharp-libvips-linux-arm64": "1.2.3", - "@img/sharp-libvips-linux-ppc64": "1.2.3", - "@img/sharp-libvips-linux-s390x": "1.2.3", - "@img/sharp-libvips-linux-x64": "1.2.3", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", - "@img/sharp-libvips-linuxmusl-x64": "1.2.3", - "@img/sharp-linux-arm": "0.34.4", - "@img/sharp-linux-arm64": "0.34.4", - "@img/sharp-linux-ppc64": "0.34.4", - "@img/sharp-linux-s390x": "0.34.4", - "@img/sharp-linux-x64": "0.34.4", - "@img/sharp-linuxmusl-arm64": "0.34.4", - "@img/sharp-linuxmusl-x64": "0.34.4", - "@img/sharp-wasm32": "0.34.4", - "@img/sharp-win32-arm64": "0.34.4", - "@img/sharp-win32-ia32": "0.34.4", - "@img/sharp-win32-x64": "0.34.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/sonner": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz", - "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spamc": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/spamc/-/spamc-0.0.5.tgz", - "integrity": "sha512-jYXItuZuiWZyG9fIdvgTUbp2MNRuyhuSwvvhhpPJd4JK/9oSZxkD7zAj53GJtowSlXwCJzLg6sCKAoE9wXsKgg==", - "dev": true - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "license": "MIT" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stripe": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.2.1.tgz", - "integrity": "sha512-GwB1B7WSwEBzW4dilgyJruUYhbGMscrwuyHsPUmSRKrGHZ5poSh2oU9XKdii5BFVJzXHn35geRvGJ6R8bYcp8w==", - "license": "MIT", - "dependencies": { - "qs": "^6.11.0" - }, - "engines": { - "node": ">=12.*" - }, - "peerDependencies": { - "@types/node": ">=12.x.x" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/sucrase/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.1.tgz", - "integrity": "sha512-qyjpz0qgcomRr41a5Aye42o69TKwCeHM9F8htLGVeUMKekNS6qAqz9oS7CtSvgGJSppSNAYAIh7vrfrSdHj9zw==", - "license": "Apache-2.0", - "dependencies": { - "@scarf/scarf": "=1.4.0" - } - }, - "node_modules/swagger-ui-express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", - "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "license": "MIT", - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, - "node_modules/tailwind-merge": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", - "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", - "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", - "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/terser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", - "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tiny-lru": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.3.4.tgz", - "integrity": "sha512-UxWEfRKpFCabAf6fkTNdlfSw/RDUJ/4C6i1aLZaDnGF82PERHyYhz5CMCVYXtLt34LbqgfpJ2bjmgGKgxuF/6A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tsc-alias": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", - "integrity": "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.3", - "commander": "^9.0.0", - "get-tsconfig": "^4.10.0", - "globby": "^11.0.4", - "mylas": "^2.1.9", - "normalize-path": "^3.0.0", - "plimit-lit": "^1.2.6" - }, - "bin": { - "tsc-alias": "dist/bin/index.js" - }, - "engines": { - "node": ">=16.20.2" - } - }, - "node_modules/tsc-alias/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tsc-alias/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/tsc-alias/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tsc-alias/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tsc-alias/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "license": "MIT", - "engines": { - "node": ">=0.6.x" - } - }, - "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tsyringe": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", - "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", - "license": "MIT", - "dependencies": { - "tslib": "^1.9.3" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/tsyringe/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tw-animate-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Wombosvideo" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz", - "integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.0", - "@typescript-eslint/parser": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0", - "@typescript-eslint/utils": "8.46.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-debounce": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.4.tgz", - "integrity": "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16.0.0" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/use-intl": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.9.tgz", - "integrity": "sha512-bZu+h13HIgOvsoGleQtUe4E6gM49CRm+AH36KnJVB/qb1+Beo7jr7HNrR8YWH8oaOkQfGNm6vh0HTepxng8UTg==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "^2.2.0", - "@schummar/icu-type-parser": "1.21.5", - "intl-messageformat": "^10.5.14" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vaul": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", - "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webpack": { - "version": "5.102.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.0.tgz", - "integrity": "sha512-hUtqAR3ZLVEYDEABdBioQCIqSoguHbFn1K7WlPPWSuXmx0031BD73PSE35jKyftdSh4YLDoQNgK4pqBt5Q82MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.3", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.2.3", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", - "license": "MIT", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.7.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.9.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-daily-rotate-file": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", - "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", - "license": "MIT", - "dependencies": { - "file-stream-rotator": "^0.6.1", - "object-hash": "^3.0.0", - "triple-beam": "^1.4.1", - "winston-transport": "^4.7.0" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "winston": "^3" - } - }, - "node_modules/winston-transport": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", - "dependencies": { - "logform": "^2.7.0", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reodotdev": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reodotdev/-/reodotdev-1.0.0.tgz", + "integrity": "sha512-wXe1vJucZjrhQL0SxOL9EvmJrtbMCIEGMdZX5lj/57n2T3UhBHZsAcM5TQASJ0T6ZBbrETRnMhH33bsbJeRO6Q==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resend": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.1.2.tgz", + "integrity": "sha512-C9Q+YkRe57P8MQlkHG3yatSR/B6sqBGA06Ri2DveJfkz9Vm16182FC/iHB0K6IAfmqZ4yRrFebFw1EPuktLtSg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@react-email/render": "*" + }, + "peerDependenciesMeta": { + "@react-email/render": { + "optional": true + } + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "license": "MIT", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/sonner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz", + "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spamc": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/spamc/-/spamc-0.0.5.tgz", + "integrity": "sha512-jYXItuZuiWZyG9fIdvgTUbp2MNRuyhuSwvvhhpPJd4JK/9oSZxkD7zAj53GJtowSlXwCJzLg6sCKAoE9wXsKgg==", + "dev": true + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "license": "MIT" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.2.1.tgz", + "integrity": "sha512-GwB1B7WSwEBzW4dilgyJruUYhbGMscrwuyHsPUmSRKrGHZ5poSh2oU9XKdii5BFVJzXHn35geRvGJ6R8bYcp8w==", + "license": "MIT", + "dependencies": { + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + }, + "peerDependencies": { + "@types/node": ">=12.x.x" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/sucrase/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.29.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.3.tgz", + "integrity": "sha512-U99f/2YocRA2Mxqx3eUBRhQonWVtE5dIvMs0Zlsn4a4ip8awMq0JxXhU+Sidtna2WlZrHbK2Rro3RZvYUymRbA==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", + "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/terser": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-lru": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.3.4.tgz", + "integrity": "sha512-UxWEfRKpFCabAf6fkTNdlfSw/RDUJ/4C6i1aLZaDnGF82PERHyYhz5CMCVYXtLt34LbqgfpJ2bjmgGKgxuF/6A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsc-alias": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", + "integrity": "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "get-tsconfig": "^4.10.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" + }, + "engines": { + "node": ">=16.20.2" + } + }, + "node_modules/tsc-alias/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/tsc-alias/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tsc-alias/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tsc-alias/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz", + "integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.46.0", + "@typescript-eslint/parser": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-debounce": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.4.tgz", + "integrity": "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/use-intl": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.11.tgz", + "integrity": "sha512-cSOPKwVjaB5Y22vnrVMio5qRolBADe+TYiqsW0/jLqRn6MUNRNBparEDfx12F170ruBWhzcdW6+v6j+MlWGbEQ==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "^2.2.0", + "@schummar/icu-type-parser": "1.21.5", + "intl-messageformat": "^10.5.14" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack": { + "version": "5.102.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", + "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.26.3", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", + "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", + "license": "MIT", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^3.0.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz", + "integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0" + } } - } - }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz", - "integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "zod": "^3.25.0" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz", - "integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz", - "integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz", - "integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz", - "integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz", - "integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz", - "integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } - } } From 02b1de32661e70c1176a78e273e0a0949a054872 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 8 Oct 2025 12:06:48 -0700 Subject: [PATCH 201/322] Make sure siteIds are numbers Fixes PAN-145 --- server/routers/client/updateClient.ts | 2 +- src/app/[orgId]/settings/clients/[clientId]/general/page.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index f60f14a0..80050f6c 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -29,7 +29,7 @@ const updateClientSchema = z .object({ name: z.string().min(1).max(255).optional(), siteIds: z - .array(z.string().transform(Number).pipe(z.number())) + .array(z.number().int().positive()) .optional() }) .strict(); diff --git a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx b/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx index 55d7c0d3..c7171c8d 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx +++ b/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx @@ -115,7 +115,7 @@ export default function GeneralPage() { try { await api.post(`/client/${client?.clientId}`, { name: data.name, - siteIds: data.siteIds.map(site => site.id) + siteIds: data.siteIds.map(site => parseInt(site.id)) }); updateClient({ name: data.name }); From 1b01c4f0533c77d4bda0422f8717abdd69d2618c Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 8 Oct 2025 14:00:05 -0700 Subject: [PATCH 202/322] fix idp infinite redirect closes #1540 --- src/app/auth/resource/[resourceGuid]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index 3eaf1e86..53efef18 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -207,7 +207,7 @@ export default async function ResourceAuthPage(props: { })) as LoginFormIDP[]; } - if (authInfo.skipToIdpId && authInfo.skipToIdpId !== null) { + if (!userIsUnauthorized && isSSOOnly && authInfo.skipToIdpId && authInfo.skipToIdpId !== null) { const idp = loginIdps.find((idp) => idp.idpId === authInfo.skipToIdpId); if (idp) { return ( From 526307e192e1dea70b5d65933ba49b5462e2c422 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 8 Oct 2025 16:43:40 -0700 Subject: [PATCH 203/322] Fix ssl undefined issue --- server/routers/resource/getResourceAuthInfo.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index b7775251..834da7b3 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -103,18 +103,18 @@ export async function getResourceAuthInfo( .limit(1); const resource = result?.resources; - const pincode = result?.resourcePincode; - const password = result?.resourcePassword; - const headerAuth = result?.resourceHeaderAuth; - - const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`; - if (!resource) { return next( createHttpError(HttpCode.NOT_FOUND, "Resource not found") ); } + const pincode = result?.resourcePincode; + const password = result?.resourcePassword; + const headerAuth = result?.resourceHeaderAuth; + + const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`; + return response(res, { data: { niceId: resource.niceId, From e0996a17ef43c0ecb9882ad1db7c6ab8621e1e51 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 8 Oct 2025 17:34:53 -0700 Subject: [PATCH 204/322] rename managed nodes --- .gitignore | 3 ++- messages/en-US.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 661c32f9..23fdd106 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ server/db/index.ts server/build.ts postgres/ dynamic/ -*.mmdb \ No newline at end of file +*.mmdb +scratch/ diff --git a/messages/en-US.json b/messages/en-US.json index 27179b50..9435b5d7 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1542,8 +1542,8 @@ "autoLoginError": "Auto Login Error", "autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.", "autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.", - "remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted", - "remoteExitNodeDescription": "Manage nodes to extend your network connectivity", + "remoteExitNodeManageRemoteExitNodes": "Managed Nodes", + "remoteExitNodeDescription": "Self-host one or more nodes for tunnel exit servers", "remoteExitNodes": "Nodes", "searchRemoteExitNodes": "Search nodes...", "remoteExitNodeAdd": "Add Node", From e601038c0f35113996e20607fc19ab6a815dd28c Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 8 Oct 2025 17:49:30 -0700 Subject: [PATCH 205/322] fix role extraction in idp form --- .../settings/(private)/idp/[idpId]/general/page.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx index 59f7aa85..d4bfdcd0 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -144,7 +144,7 @@ export default function GeneralPage() { }; const form = useForm({ - resolver: zodResolver(getFormSchema()) as any, // is this right? + resolver: zodResolver(getFormSchema()) as any, // is this right? defaultValues: { name: "", clientId: "", @@ -236,17 +236,11 @@ export default function GeneralPage() { let tenantId = ""; if (idpVariant === "azure" && data.idpOidcConfig?.authUrl) { // Azure URL format: https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize - console.log( - "Azure authUrl:", - data.idpOidcConfig.authUrl - ); const tenantMatch = data.idpOidcConfig.authUrl.match( /login\.microsoftonline\.com\/([^\/]+)\/oauth2/ ); - console.log("Tenant match:", tenantMatch); if (tenantMatch) { tenantId = tenantMatch[1]; - console.log("Extracted tenantId:", tenantId); } } @@ -262,8 +256,6 @@ export default function GeneralPage() { : matchingRoleId || null }; - console.log(formData); - // Add variant-specific fields if (idpVariant === "oidc") { formData.authUrl = data.idpOidcConfig.authUrl; @@ -276,7 +268,6 @@ export default function GeneralPage() { formData.scopes = data.idpOidcConfig.scopes; } else if (idpVariant === "azure") { formData.tenantId = tenantId; - console.log("Setting tenantId in formData:", tenantId); } form.reset(formData); @@ -284,7 +275,7 @@ export default function GeneralPage() { // Set the role mapping mode based on the data // Default to "expression" unless it's a simple roleId or basic '{role name}' pattern setRoleMappingMode( - isRoleId || isRoleName ? "role" : "expression" + matchingRoleId && isRoleName ? "role" : "expression" ); } } catch (e) { From 01db51969145b315053efc4be721cbcdb078bedf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 01:22:20 +0000 Subject: [PATCH 206/322] Bump golang.org/x/term in /install in the prod-minor-updates group Bumps the prod-minor-updates group in /install with 1 update: [golang.org/x/term](https://github.com/golang/term). Updates `golang.org/x/term` from 0.35.0 to 0.36.0 - [Commits](https://github.com/golang/term/compare/v0.35.0...v0.36.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-version: 0.36.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- install/go.mod | 4 ++-- install/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/install/go.mod b/install/go.mod index b1465ac5..8c6e06e2 100644 --- a/install/go.mod +++ b/install/go.mod @@ -3,8 +3,8 @@ module installer go 1.24.0 require ( - golang.org/x/term v0.35.0 + golang.org/x/term v0.36.0 gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/sys v0.36.0 // indirect +require golang.org/x/sys v0.37.0 // indirect diff --git a/install/go.sum b/install/go.sum index 789a291b..68e246d1 100644 --- a/install/go.sum +++ b/install/go.sum @@ -1,7 +1,7 @@ -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From bad88e47416963b965101510a895ae5a3f65aff5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 01:26:12 +0000 Subject: [PATCH 207/322] Bump the prod-minor-updates group with 2 updates Bumps the prod-minor-updates group with 2 updates: [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) and [react-easy-sort](https://github.com/ValentinH/react-easy-sort). Updates `@aws-sdk/client-s3` from 3.901.0 to 3.906.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.906.0/clients/client-s3) Updates `react-easy-sort` from 1.7.0 to 1.8.0 - [Release notes](https://github.com/ValentinH/react-easy-sort/releases) - [Commits](https://github.com/ValentinH/react-easy-sort/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.906.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react-easy-sort dependency-version: 1.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 217 +++++++++++++++++++++------------------------- package.json | 4 +- 2 files changed, 101 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17b9684a..c6f04ab4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.901.0", + "@aws-sdk/client-s3": "3.906.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -82,7 +82,7 @@ "qrcode.react": "4.2.0", "react": "19.2.0", "react-dom": "19.2.0", - "react-easy-sort": "^1.7.0", + "react-easy-sort": "^1.8.0", "react-hook-form": "7.64.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", @@ -379,32 +379,32 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.901.0.tgz", - "integrity": "sha512-wyKhZ51ur1tFuguZ6PgrUsot9KopqD0Tmxw8O8P/N3suQDxFPr0Yo7Y77ezDRDZQ95Ml3C0jlvx79HCo8VxdWA==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.906.0.tgz", + "integrity": "sha512-6JQGrmQBHjnARQR+HSaj8DvLRbXTpPa8knYi1veT709JHXVkCkNNLKs7ULjVNCpSffRpzVYJn+eONHKj3Y0knQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.901.0", - "@aws-sdk/credential-provider-node": "3.901.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-node": "3.906.0", "@aws-sdk/middleware-bucket-endpoint": "3.901.0", "@aws-sdk/middleware-expect-continue": "3.901.0", - "@aws-sdk/middleware-flexible-checksums": "3.901.0", + "@aws-sdk/middleware-flexible-checksums": "3.906.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-location-constraint": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/middleware-sdk-s3": "3.906.0", "@aws-sdk/middleware-ssec": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", "@aws-sdk/region-config-resolver": "3.901.0", - "@aws-sdk/signature-v4-multi-region": "3.901.0", + "@aws-sdk/signature-v4-multi-region": "3.906.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", "@aws-sdk/xml-builder": "3.901.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.14.0", @@ -1001,23 +1001,23 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.901.0.tgz", - "integrity": "sha512-sGyDjjkJ7ppaE+bAKL/Q5IvVCxtoyBIzN+7+hWTS/mUxWJ9EOq9238IqmVIIK6sYNIzEf9yhobfMARasPYVTNg==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.906.0.tgz", + "integrity": "sha512-GGDwjW2cLzoEF5A1tBlZQZXzhlZzuM6cKNbSxUsCcBXtPAX03eb2GKApVy1SzpD03nTJk5T6GicGAm+BzK+lEg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.14.0", "@smithy/fetch-http-handler": "^5.3.0", @@ -1050,9 +1050,9 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.901.0.tgz", - "integrity": "sha512-brKAc3y64tdhyuEf+OPIUln86bRTqkLgb9xkd6kUdIeA5+qmp/N6amItQz+RN4k4O3kqkCPYnAd3LonTKluobw==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.906.0.tgz", + "integrity": "sha512-+FuwAcozee8joVfjwly/8kSFNCvQOkcQYjINUckqBkdjO4iCRfOgSaz+0JMpMcYgVPnnyZv62gJ2g0bj0U+YDQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.901.0", @@ -1074,12 +1074,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.901.0.tgz", - "integrity": "sha512-5hAdVl3tBuARh3zX5MLJ1P/d+Kr5kXtDU3xm1pxUEF4xt2XkEEpwiX5fbkNkz2rbh3BCt2gOHsAbh6b3M7n+DA==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.906.0.tgz", + "integrity": "sha512-vtMDguMci2aXhkgEqg1iqyQ7vVcafpx9uypksM6FQsNr3Cc/8I6HgfBAja6BuPwkaCn9NoMnG0/iuuOWr8P9dg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/types": "^4.6.0", @@ -1090,12 +1090,12 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.901.0.tgz", - "integrity": "sha512-Ggr7+0M6QZEsrqRkK7iyJLf4LkIAacAxHz9c4dm9hnDdU7vqrlJm6g73IxMJXWN1bIV7IxfpzB11DsRrB/oNjQ==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.906.0.tgz", + "integrity": "sha512-L97N2SUkZp03s1LJZ1sCkUaUZ7m9T72faaadn05wyst/iXonSZKPHYMQVWGYhTC2OtRV0FQvBXIAqFZsNGQD0Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/fetch-http-handler": "^5.3.0", "@smithy/node-http-handler": "^4.3.0", @@ -1111,18 +1111,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.901.0.tgz", - "integrity": "sha512-zxadcDS0hNJgv8n4hFYJNOXyfjaNE1vvqIiF/JzZSQpSSYXzCd+WxXef5bQh+W3giDtRUmkvP5JLbamEFjZKyw==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.906.0.tgz", + "integrity": "sha512-r7TbHD80WXo42kTEC5bqa4b87ho3T3yd2VEKo1qbEmOUovocntO8HC3JxHYr0XSeZ82DEYxLARb84akWjabPzg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", - "@aws-sdk/credential-provider-env": "3.901.0", - "@aws-sdk/credential-provider-http": "3.901.0", - "@aws-sdk/credential-provider-process": "3.901.0", - "@aws-sdk/credential-provider-sso": "3.901.0", - "@aws-sdk/credential-provider-web-identity": "3.901.0", - "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", @@ -1135,17 +1135,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.901.0.tgz", - "integrity": "sha512-dPuFzMF7L1s/lQyT3wDxqLe82PyTH+5o1jdfseTEln64LJMl0ZMWaKX/C1UFNDxaTd35Cgt1bDbjjAWHMiKSFQ==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.906.0.tgz", + "integrity": "sha512-xga127vP0rFxiHjEUjLe6Yf4hQ/AZinOF4AqQr/asWQO+/uwh3aH8nXcS4lkpZNygxMHbuNXm7Xg504GKCMlLQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.901.0", - "@aws-sdk/credential-provider-http": "3.901.0", - "@aws-sdk/credential-provider-ini": "3.901.0", - "@aws-sdk/credential-provider-process": "3.901.0", - "@aws-sdk/credential-provider-sso": "3.901.0", - "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-ini": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", @@ -1158,12 +1158,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.901.0.tgz", - "integrity": "sha512-/IWgmgM3Cl1wTdJA5HqKMAojxLkYchh5kDuphApxKhupLu6Pu0JBOHU8A5GGeFvOycyaVwosod6zDduINZxe+A==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.906.0.tgz", + "integrity": "sha512-P8R4GpDLppe+8mp+SOj1fKaY3AwDULCi/fqMSJjvf8qN6OM+vGGpFP3iXvkjFYyyV+8nRXY+HQCLRoZKpRtzMg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1175,14 +1175,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.901.0.tgz", - "integrity": "sha512-SjmqZQHmqFSET7+6xcZgtH7yEyh5q53LN87GqwYlJZ6KJ5oNw11acUNEhUOL1xTSJEvaWqwTIkS2zqrzLcM9bw==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.906.0.tgz", + "integrity": "sha512-wYljHU7yNEzt7ngZZ21FWh+RlO16gTpWvXyRqlryuCgIWugHD8bl7JphGnUN1md5/v+mCRuGK58JoFGZq+qrjA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.901.0", - "@aws-sdk/core": "3.901.0", - "@aws-sdk/token-providers": "3.901.0", + "@aws-sdk/client-sso": "3.906.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/token-providers": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1194,13 +1194,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.901.0.tgz", - "integrity": "sha512-NYjy/6NLxH9m01+pfpB4ql8QgAorJcu8tw69kzHwUd/ql6wUDTbC7HcXqtKlIwWjzjgj2BKL7j6SyFapgCuafA==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.906.0.tgz", + "integrity": "sha512-V9PurepVko8+iyEvI9WAlk5dXJ1uWIW03RPLnNBEmeCqFjjit16HrNaaVvnp9fQbG7CSKSGqK026SjDgtKGKYA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", - "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1245,15 +1245,15 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.901.0.tgz", - "integrity": "sha512-63lcKfggVUFyXhE4SsFXShCTCyh7ZHEqXLyYEL4DwX+VWtxutf9t9m3fF0TNUYDE8eEGWiRXhegj8l4FjuW+wA==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.906.0.tgz", + "integrity": "sha512-vbOf5Pf2bRjw+Is1OsUKKP88uPKES8/B3c3yq0B72Y4ZgZEDymXIxGvZYPkThLk266PH7eHo+ZneZjkdfz6Zbg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", @@ -1328,12 +1328,12 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.901.0.tgz", - "integrity": "sha512-prgjVC3fDT2VIlmQPiw/cLee8r4frTam9GILRUVQyDdNtshNwV3MiaSCLzzQJjKJlLgnBLNUHJCSmvUVtg+3iA==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.906.0.tgz", + "integrity": "sha512-8Ztl5natyVXOvpk/en2j9Bjn2t8vawjbvgcU0/ZF5/JtA1rKSTctRXusICJgCovFHzaAH2MVhA51nnp3d8rViA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/core": "^3.14.0", @@ -1367,12 +1367,12 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.901.0.tgz", - "integrity": "sha512-Zby4F03fvD9xAgXGPywyk4bC1jCbnyubMEYChLYohD+x20ULQCf+AimF/Btn7YL+hBpzh1+RmqmvZcx+RgwgNQ==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.906.0.tgz", + "integrity": "sha512-CMAjq2oCEv5EEvmlFvio8t4KQL2jGORyDQu7oLj4l0a2biPgxbwL3utalbm9yKty1rQM5zKpaa7id7ZG3X1f6A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@smithy/core": "^3.14.0", @@ -1385,23 +1385,23 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.901.0.tgz", - "integrity": "sha512-feAAAMsVwctk2Tms40ONybvpfJPLCmSdI+G+OTrNpizkGLNl6ik2Ng2RzxY6UqOfN8abqKP/DOUj1qYDRDG8ag==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.906.0.tgz", + "integrity": "sha512-0/r0bh/9Bm14lVe+jAzQQB2ufq9S4Vd9Wg5rZn8RhrhKl6y/DC1aRzOo2kJTNu5pCbVfQsd/VXLLnkcbOrDy6A==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.901.0", + "@aws-sdk/core": "3.906.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.14.0", "@smithy/fetch-http-handler": "^5.3.0", @@ -1451,12 +1451,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.901.0.tgz", - "integrity": "sha512-2IWxbll/pRucp1WQkHi2W5E2SVPGBvk4Is923H7gpNksbVFws18ItjMM8ZpGm44cJEoy1zR5gjhLFklatpuoOw==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.906.0.tgz", + "integrity": "sha512-zqxRN8/dSrAaAEi5oXIeScsrbDkS63+ZyaBrkC6bc8Jd/bCvJM6D4LjJJxIOPBNXuF0bNhBIlTmqwtbkiqCwZw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/middleware-sdk-s3": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/protocol-http": "^5.3.0", "@smithy/signature-v4": "^5.3.0", @@ -1468,13 +1468,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.901.0.tgz", - "integrity": "sha512-pJEr1Ggbc/uVTDqp9IbNu9hdr0eQf3yZix3s4Nnyvmg4xmJSGAlbPC9LrNr5u3CDZoc8Z9CuLrvbP4MwYquNpQ==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.906.0.tgz", + "integrity": "sha512-gdxXleCjMUAKnyR/1ksdnv3Fuifr9iuaeEtINRHkwVluwcORabEdOlxW36th2QdkpTTyP1hW35VATz2R6v/i2Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.901.0", - "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1551,12 +1551,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.901.0.tgz", - "integrity": "sha512-l59KQP5TY7vPVUfEURc7P5BJKuNg1RSsAKBQW7LHLECXjLqDUbo2SMLrexLBEoArSt6E8QOrIN0C8z/0Xk0jYw==", + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.906.0.tgz", + "integrity": "sha512-9Gaglw80E9UZ5FctCp5pZAzT40/vC4Oo0fcNXsfplLkpWqTU+NTdTRMYe3TMZ1/v1/JZKuGUVyHiuo/xLu3NmA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", "@aws-sdk/types": "3.901.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", @@ -7319,18 +7319,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-move": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz", - "integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -16277,28 +16265,21 @@ } }, "node_modules/react-easy-sort": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-1.7.0.tgz", - "integrity": "sha512-82I63kXdawFhhlFrWPrI74DL48v2LKs7e7PLf5le2E/eIR9+XyCEdL4Pyjbru8XjvtQ60mPLb6oextc4PPR8Lg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-1.8.0.tgz", + "integrity": "sha512-6CUvG0rPyO8H9MTel38r/gmPemIKcOSkvgZQtrxILYFPfGZnmkLVU3YSVHEg22D+pJMoeVRdJpuF2kD2dqeIEw==", "license": "MIT", "dependencies": { - "array-move": "^3.0.1", - "tslib": "2.0.1" + "tslib": "^2.8.1" }, "engines": { - "node": ">=16" + "node": ">=18" }, "peerDependencies": { "react": ">=16.4.0", "react-dom": ">=16.4.0" } }, - "node_modules/react-easy-sort/node_modules/tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "license": "0BSD" - }, "node_modules/react-email": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.3.0.tgz", diff --git a/package.json b/package.json index 6a13f55b..dc567e23 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.901.0", + "@aws-sdk/client-s3": "3.906.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -105,7 +105,7 @@ "qrcode.react": "4.2.0", "react": "19.2.0", "react-dom": "19.2.0", - "react-easy-sort": "^1.7.0", + "react-easy-sort": "^1.8.0", "react-hook-form": "7.64.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", From b6f8ed1e4a89363f2df8b3bca6360aec81f32b69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 01:21:19 +0000 Subject: [PATCH 208/322] Bump the prod-patch-updates group across 1 directory with 3 updates Bumps the prod-patch-updates group with 3 updates in the / directory: [next-intl](https://github.com/amannn/next-intl), [npm](https://github.com/npm/cli) and [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node). Updates `next-intl` from 4.3.11 to 4.3.12 - [Release notes](https://github.com/amannn/next-intl/releases) - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v4.3.11...v4.3.12) Updates `npm` from 11.6.1 to 11.6.2 - [Release notes](https://github.com/npm/cli/releases) - [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md) - [Commits](https://github.com/npm/cli/compare/v11.6.1...v11.6.2) Updates `posthog-node` from 5.9.3 to 5.9.5 - [Release notes](https://github.com/PostHog/posthog-js/releases) - [Changelog](https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md) - [Commits](https://github.com/PostHog/posthog-js/commits/posthog-node@5.9.5/packages/node) --- updated-dependencies: - dependency-name: next-intl dependency-version: 4.3.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: npm dependency-version: 11.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: posthog-node dependency-version: 5.9.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 166 +++++++++++++++++++++------------------------- package.json | 6 +- 2 files changed, 79 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17b9684a..eeb7b7e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,15 +70,15 @@ "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", - "next-intl": "^4.3.11", + "next-intl": "^4.3.12", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.9", - "npm": "^11.6.1", + "npm": "^11.6.2", "oslo": "1.2.1", "pg": "^8.16.2", - "posthog-node": "^5.9.3", + "posthog-node": "^5.9.5", "qrcode.react": "4.2.0", "react": "19.2.0", "react-dom": "19.2.0", @@ -2219,9 +2219,9 @@ "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.5.tgz", - "integrity": "sha512-1HTESOq1IUa23g1lFZEGIXsfZKZOwWmB9RROwGn+xariiQnd++wwTMvlRAbZ8wtXRHFUamJPxsKcxpSzeCvFWQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", + "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "2.2.7", @@ -2249,23 +2249,23 @@ } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.3.tgz", - "integrity": "sha512-H/KfWSosaiDiOaW4nHe1Fn4Cgzm+oFQ8giTmB5RJzTBNSMmd+j2NVrvvZHAmlxJHcuOelzKBLjQ2EDcyH4NSWw==", + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.5", - "@formatjs/icu-skeleton-parser": "1.8.15", + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", "tslib": "^2.8.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.15", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.15.tgz", - "integrity": "sha512-qNrKxWJmnWxin5U4A4Evy7C0rgRiNw3IqXu9OGuT31B8lDxBGl+OgT8kcq0ZVKK0gqA4l4SQB9x+SFAvLT5hcQ==", + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.5", + "@formatjs/ecma402-abstract": "2.3.6", "tslib": "^2.8.0" } }, @@ -3403,9 +3403,9 @@ } }, "node_modules/@posthog/core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.2.2.tgz", - "integrity": "sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.2.4.tgz", + "integrity": "sha512-o2TkycuV98PtAkcqE8B1DJv5LBvHEDTWirK5TlkQMeF2MJg0BYliY95CeRZFILNgZJCbI3k/fhahSMRQlpXOMg==", "license": "MIT" }, "node_modules/@radix-ui/colors": { @@ -11028,14 +11028,14 @@ } }, "node_modules/intl-messageformat": { - "version": "10.7.17", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.17.tgz", - "integrity": "sha512-0Ugaf65B2J76rb31drgNF1l6bGEDkbIiYc2Glx6jaZINHnwa5kDRGy8KXYuA+/8P4G0c9prAFhfVhQJJfzUuvQ==", + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.5", + "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.3", + "@formatjs/icu-messageformat-parser": "2.11.4", "tslib": "^2.8.0" } }, @@ -12485,9 +12485,9 @@ } }, "node_modules/next-intl": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.11.tgz", - "integrity": "sha512-kyjeGUuLBU1DqDVAzhgoltYxQ8esVqqqkq2BRKPFxTwHPT9r5P5ZHePu3esjc5B3dVeVC/yFf8ebEnaYo68q1g==", + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.12.tgz", + "integrity": "sha512-yAmrQ3yx0zpNva/knniDvam3jT2d01Lv2aRgRxUIDL9zm9O4AsDjWbDIxX13t5RNf0KVnKkxH+iRcqEAmWecPg==", "funding": [ { "type": "individual", @@ -12498,7 +12498,7 @@ "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", - "use-intl": "^4.3.11" + "use-intl": "^4.3.12" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", @@ -12659,9 +12659,9 @@ } }, "node_modules/npm": { - "version": "11.6.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", - "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.2.tgz", + "integrity": "sha512-7iKzNfy8lWYs3zq4oFPa8EXZz5xt9gQNKJZau3B1ErLBb6bF7sBJ00x09485DOvRT2l5Gerbl3VlZNT57MxJVA==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -12705,7 +12705,6 @@ "ms", "node-gyp", "nopt", - "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", @@ -12740,8 +12739,8 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.5", - "@npmcli/config": "^10.4.1", + "@npmcli/arborist": "^9.1.6", + "@npmcli/config": "^10.4.2", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.1", @@ -12753,24 +12752,24 @@ "archy": "~1.0.0", "cacache": "^20.0.1", "chalk": "^5.6.2", - "ci-info": "^4.3.0", + "ci-info": "^4.3.1", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^11.0.3", "graceful-fs": "^4.2.11", - "hosted-git-info": "^9.0.0", + "hosted-git-info": "^9.0.2", "ini": "^5.0.0", "init-package-json": "^8.2.2", - "is-cidr": "^6.0.0", + "is-cidr": "^6.0.1", "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.2", - "libnpmdiff": "^8.0.8", - "libnpmexec": "^10.1.7", - "libnpmfund": "^7.0.8", + "libnpmaccess": "^10.0.3", + "libnpmdiff": "^8.0.9", + "libnpmexec": "^10.1.8", + "libnpmfund": "^7.0.9", "libnpmorg": "^8.0.1", - "libnpmpack": "^9.0.8", - "libnpmpublish": "^11.1.1", + "libnpmpack": "^9.0.9", + "libnpmpublish": "^11.1.2", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.2", @@ -12781,10 +12780,9 @@ "ms": "^2.1.2", "node-gyp": "^11.4.2", "nopt": "^8.1.0", - "normalize-package-data": "^8.0.0", "npm-audit-report": "^6.0.0", "npm-install-checks": "^7.1.2", - "npm-package-arg": "^13.0.0", + "npm-package-arg": "^13.0.1", "npm-pick-manifest": "^11.0.1", "npm-profile": "^12.0.0", "npm-registry-fetch": "^19.0.0", @@ -12795,7 +12793,7 @@ "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", "read": "^4.1.0", - "semver": "^7.7.2", + "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", "supports-color": "^10.2.2", @@ -12940,7 +12938,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.5", + "version": "9.1.6", "inBundle": true, "license": "ISC", "dependencies": { @@ -12986,7 +12984,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.1", + "version": "10.4.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -13192,14 +13190,14 @@ } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "4.0.0", + "version": "4.0.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.0.0", "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.0", + "make-fetch-happen": "^15.0.2", "proc-log": "^5.0.0", "promise-retry": "^2.0.1" }, @@ -13391,7 +13389,7 @@ } }, "node_modules/npm/node_modules/ci-info": { - "version": "4.3.0", + "version": "4.3.1", "funding": [ { "type": "github", @@ -13405,11 +13403,11 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "5.0.0", + "version": "5.0.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "ip-regex": "^5.0.0" + "ip-regex": "5.0.0" }, "engines": { "node": ">=20" @@ -13622,7 +13620,7 @@ "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "9.0.0", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -13737,11 +13735,11 @@ } }, "node_modules/npm/node_modules/is-cidr": { - "version": "6.0.0", + "version": "6.0.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^5.0.0" + "cidr-regex": "5.0.1" }, "engines": { "node": ">=20" @@ -13812,7 +13810,7 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "10.0.2", + "version": "10.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -13824,11 +13822,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.8", + "version": "8.0.9", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.5", + "@npmcli/arborist": "^9.1.6", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", @@ -13842,11 +13840,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.7", + "version": "10.1.8", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.5", + "@npmcli/arborist": "^9.1.6", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", @@ -13864,11 +13862,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.8", + "version": "7.0.9", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.5" + "@npmcli/arborist": "^9.1.6" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -13887,11 +13885,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.8", + "version": "9.0.9", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.5", + "@npmcli/arborist": "^9.1.6", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" @@ -13901,7 +13899,7 @@ } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.1.1", + "version": "11.1.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -13957,7 +13955,7 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "11.2.1", + "version": "11.2.2", "inBundle": true, "license": "ISC", "engines": { @@ -14294,19 +14292,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "8.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^9.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm/node_modules/npm-audit-report": { "version": "6.0.0", "inBundle": true, @@ -14346,7 +14331,7 @@ } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "13.0.0", + "version": "13.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -14360,11 +14345,12 @@ } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.1", + "version": "10.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "ignore-walk": "^8.0.0" + "ignore-walk": "^8.0.0", + "proc-log": "^5.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -14612,7 +14598,7 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.7.2", + "version": "7.7.3", "inBundle": true, "license": "ISC", "bin": { @@ -15949,12 +15935,12 @@ } }, "node_modules/posthog-node": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.3.tgz", - "integrity": "sha512-YA32jx4MfL5uLiVU4ynNoSzRqAigG11pkbwEI3UlqyWJpWnp8Nvpzh59b8lpmm2+5Y+kJ99sKyWLGnNZ7K1ryA==", + "version": "5.9.5", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.9.5.tgz", + "integrity": "sha512-Rv82jMVhnxlBNf8wDbP+iAJdZrhU0aHul0LaFrQ/JGxxDiK3EkclIqr+QUwA9CulleTtXf6AIFz22tLvbVs/HA==", "license": "MIT", "dependencies": { - "@posthog/core": "1.2.2" + "@posthog/core": "1.2.4" }, "engines": { "node": ">=20" @@ -18948,9 +18934,9 @@ } }, "node_modules/use-intl": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.11.tgz", - "integrity": "sha512-cSOPKwVjaB5Y22vnrVMio5qRolBADe+TYiqsW0/jLqRn6MUNRNBparEDfx12F170ruBWhzcdW6+v6j+MlWGbEQ==", + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.12.tgz", + "integrity": "sha512-RxW2/D17irlDOJOzClKl+kWA7ReGLpo/A8f/LF7w1kIxO6mPKVh422JJ/pDCcvtYFCI4aPtn1AXUfELKbM+7tg==", "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "^2.2.0", diff --git a/package.json b/package.json index 6a13f55b..ce9b73fb 100644 --- a/package.json +++ b/package.json @@ -93,15 +93,15 @@ "maxmind": "5.0.0", "moment": "2.30.1", "next": "15.5.4", - "next-intl": "^4.3.11", + "next-intl": "^4.3.12", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.9", - "npm": "^11.6.1", + "npm": "^11.6.2", "oslo": "1.2.1", "pg": "^8.16.2", - "posthog-node": "^5.9.3", + "posthog-node": "^5.9.5", "qrcode.react": "4.2.0", "react": "19.2.0", "react-dom": "19.2.0", From f64a477c3d3853e86648adc15c62251c83afa5e5 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Thu, 9 Oct 2025 20:21:16 -0700 Subject: [PATCH 209/322] fix spacing issue in strategy select --- src/components/StrategySelect.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/StrategySelect.tsx b/src/components/StrategySelect.tsx index 374d1bae..63f1ce53 100644 --- a/src/components/StrategySelect.tsx +++ b/src/components/StrategySelect.tsx @@ -5,7 +5,7 @@ import { RadioGroup, RadioGroupItem } from "./ui/radio-group"; import { useState, ReactNode } from "react"; export interface StrategyOption { -id: TValue; + id: TValue; title: string; description: string; disabled?: boolean; @@ -59,9 +59,9 @@ export function StrategySelect({ disabled={option.disabled} className="absolute left-4 top-5 h-4 w-4 border-primary text-primary" /> -
+
{option.icon && ( -
+
{option.icon}
)} From d92b87b7c8984e56b2ee03f8afe4958a1a0bd983 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 10 Oct 2025 11:27:15 -0700 Subject: [PATCH 210/322] Chungus 2.0 --- drizzle.pg.config.ts | 13 +- drizzle.sqlite.config.ts | 13 +- esbuild.mjs | 210 +++++++++++++- server/apiServer.ts | 14 +- server/auth/actions.ts | 1 - server/cleanup.ts | 13 + server/db/pg/privateSchema.ts | 13 - server/db/sqlite/privateSchema.ts | 13 - server/emails/sendEmail.ts | 3 +- ...ng.tsx => NotifyUsageLimitApproaching.tsx} | 13 - ...eached.tsx => NotifyUsageLimitReached.tsx} | 13 - server/index.ts | 3 + server/integrationApiServer.ts | 2 +- server/internalServer.ts | 4 +- server/lib/billing/createCustomer.ts | 6 + server/lib/{private => }/billing/features.ts | 13 - server/lib/billing/getOrgTierData.ts | 8 + server/lib/billing/index.ts | 5 + server/lib/{private => }/billing/limitSet.ts | 13 - .../{private => }/billing/limitsService.ts | 13 - server/lib/{private => }/billing/tiers.ts | 13 - .../lib/{private => }/billing/usageService.ts | 24 +- .../blueprints/applyNewtDockerBlueprint.ts | 2 +- server/lib/blueprints/proxyResources.ts | 2 +- .../{remoteCertificates => }/certificates.ts | 4 +- server/lib/config.ts | 123 -------- .../private => lib}/corsWithLoginPage.ts | 13 - .../lib/{private => }/createUserAccountOrg.ts | 19 +- server/lib/exitNodes/exitNodes.ts | 17 +- server/lib/exitNodes/index.ts | 33 +-- server/lib/private/rateLimitStore.ts | 25 -- server/lib/private/s3.ts | 19 -- server/lib/remoteCertificates/index.ts | 14 - server/lib/resend.ts | 15 + server/lib/s3.ts | 5 + server/lib/traefik/TraefikConfigManager.ts | 6 +- server/lib/traefik/index.ts | 12 +- .../auth/sessions/remoteExitNode.ts} | 0 server/private/cleanup.ts | 15 + .../lib}/billing/createCustomer.ts | 2 +- server/private/lib/billing/getOrgTierData.ts | 46 +++ .../private => private/lib}/billing/index.ts | 5 +- .../lib/certificates.ts} | 4 +- server/private/lib/config.ts | 163 +++++++++++ .../lib/exitNodes/exitNodeComms.ts} | 7 +- .../lib/exitNodes/exitNodes.ts} | 0 server/private/lib/exitNodes/index.ts | 15 + .../private => private/lib}/rateLimit.test.ts | 0 .../{db/private => private/lib}/rateLimit.ts | 8 +- server/private/lib/rateLimitStore.ts | 32 +++ .../private => private/lib}/readConfigFile.ts | 1 - server/{db/private => private/lib}/redis.ts | 8 +- .../{db/private => private/lib}/redisStore.ts | 0 server/{lib/private => private/lib}/resend.ts | 4 +- server/{lib/private => private/lib}/stripe.ts | 4 +- .../lib/traefik/getTraefikConfig.ts} | 2 +- server/private/lib/traefik/index.ts | 14 + .../private => private/middlewares}/index.ts | 2 +- .../middlewares}/verifyCertificateAccess.ts | 0 .../middlewares}/verifyIdpAccess.ts | 0 .../middlewares}/verifyLoginPageAccess.ts | 0 .../middlewares}/verifyRemoteExitNode.ts | 2 +- .../verifyRemoteExitNodeAccess.ts | 0 .../routers/auth/getSessionTransferToken.ts} | 0 server/private/routers/auth/index.ts | 16 ++ .../routers/auth/quickStart.ts} | 8 +- .../routers/auth/transferSession.ts} | 0 .../routers}/billing/createCheckoutSession.ts | 6 +- .../routers}/billing/createPortalSession.ts | 2 +- .../routers}/billing/getOrgSubscription.ts | 0 .../routers}/billing/getOrgUsage.ts | 4 +- .../billing/hooks/handleCustomerCreated.ts | 0 .../billing/hooks/handleCustomerDeleted.ts | 0 .../billing/hooks/handleCustomerUpdated.ts | 0 .../hooks/handleSubscriptionCreated.ts | 4 +- .../hooks/handleSubscriptionDeleted.ts | 2 +- .../hooks/handleSubscriptionUpdated.ts | 4 +- .../routers}/billing/index.ts | 0 .../routers}/billing/internalGetOrgTier.ts | 34 +-- .../routers}/billing/subscriptionLifecycle.ts | 4 +- .../routers}/billing/webhooks.ts | 8 +- .../certificates/createCertificate.ts | 0 .../routers}/certificates/getCertificate.ts | 0 .../routers}/certificates/index.ts | 0 .../certificates/restartCertificate.ts | 0 .../checkDomainNamespaceAvailability.ts} | 0 server/private/routers/domain/index.ts | 15 + .../routers/domain/listDomainNamespaces.ts} | 0 server/private/routers/external.ts | 262 ++++++++++++++++++ .../routers/gerbil/createExitNode.ts} | 0 .../routers/gerbil/receiveBandwidth.ts} | 0 .../private => private/routers}/hybrid.ts | 21 +- server/private/routers/integration.ts | 42 +++ server/private/routers/internal.ts | 36 +++ .../routers}/loginPage/createLoginPage.ts | 6 +- .../routers}/loginPage/deleteLoginPage.ts | 0 .../routers}/loginPage/getLoginPage.ts | 0 .../routers}/loginPage/index.ts | 0 .../routers}/loginPage/loadLoginPage.ts | 0 .../routers}/loginPage/updateLoginPage.ts | 6 +- server/private/routers/org/index.ts | 14 + .../routers/org/sendUsageNotifications.ts} | 4 +- .../routers}/orgIdp/createOrgOidcIdp.ts | 4 +- server/private/routers/orgIdp/deleteOrgIdp.ts | 108 ++++++++ .../routers}/orgIdp/getOrgIdp.ts | 0 .../routers}/orgIdp/index.ts | 3 +- .../routers}/orgIdp/listOrgIdps.ts | 0 .../routers}/orgIdp/updateOrgOidcIdp.ts | 5 +- .../remoteExitNode/createRemoteExitNode.ts | 6 +- .../remoteExitNode/deleteRemoteExitNode.ts | 4 +- .../remoteExitNode/getRemoteExitNode.ts | 0 .../remoteExitNode/getRemoteExitNodeToken.ts | 2 +- .../handleRemoteExitNodePingMessage.ts | 0 .../handleRemoteExitNodeRegisterMessage.ts | 0 .../routers}/remoteExitNode/index.ts | 0 .../remoteExitNode/listRemoteExitNodes.ts | 0 .../pickRemoteExitNodeDefaults.ts | 0 .../quickStartRemoteExitNode.ts | 0 .../private/routers/ws/index.ts | 1 + server/private/routers/ws/messageHandlers.ts | 26 ++ .../privateWs.ts => private/routers/ws/ws.ts} | 76 +---- server/routers/auth/index.ts | 5 +- server/routers/auth/requestTotpSecret.ts | 4 +- server/routers/auth/signup.ts | 4 +- server/routers/auth/verifyEmail.ts | 2 +- server/routers/badger/verifySession.ts | 4 +- server/routers/billing/webhooks.ts | 14 + .../routers/certificates/createCertificate.ts | 5 + server/routers/client/createClient.ts | 2 +- server/routers/client/targets.ts | 2 +- server/routers/client/updateClient.ts | 2 +- server/routers/domain/createOrgDomain.ts | 4 +- server/routers/domain/deleteOrgDomain.ts | 4 +- server/routers/domain/index.ts | 2 - server/routers/external.ts | 255 +---------------- server/routers/gerbil/getConfig.ts | 7 +- server/routers/gerbil/getResolvedHostname.ts | 21 +- server/routers/gerbil/peers.ts | 2 +- server/routers/gerbil/receiveBandwidth.ts | 6 +- server/routers/gerbil/updateHolePunch.ts | 3 +- server/routers/hybrid.ts | 4 + server/routers/idp/generateOidcUrl.ts | 4 +- server/routers/idp/validateOidcCallback.ts | 4 +- server/routers/integration.ts | 16 -- server/routers/internal.ts | 26 +- server/routers/newt/dockerSocket.ts | 2 +- .../newt/handleApplyBlueprintMessage.ts | 2 +- server/routers/newt/handleGetConfigMessage.ts | 4 +- .../newt/handleNewtPingRequestMessage.ts | 5 +- .../routers/newt/handleNewtRegisterMessage.ts | 8 +- .../newt/handleReceiveBandwidthMessage.ts | 2 +- server/routers/newt/handleSocketMessages.ts | 2 +- server/routers/newt/peers.ts | 2 +- server/routers/newt/targets.ts | 2 +- server/routers/olm/handleOlmPingMessage.ts | 2 +- .../routers/olm/handleOlmRegisterMessage.ts | 4 +- server/routers/olm/handleOlmRelayMessage.ts | 2 +- server/routers/olm/peers.ts | 2 +- server/routers/org/createOrg.ts | 8 +- server/routers/org/deleteOrg.ts | 2 +- server/routers/org/index.ts | 1 - server/routers/resource/createResource.ts | 2 +- server/routers/resource/updateResource.ts | 2 +- server/routers/site/createSite.ts | 3 +- server/routers/site/deleteSite.ts | 2 +- server/routers/site/pickSiteDefaults.ts | 2 +- server/routers/site/socketIntegration.ts | 2 +- .../supporterKey/isSupporterKeyVisible.ts | 3 +- .../target/handleHealthcheckStatusMessage.ts | 2 +- .../routers/traefik/traefikConfigProvider.ts | 2 +- server/routers/user/acceptInvite.ts | 4 +- server/routers/user/createOrgUser.ts | 8 +- server/routers/user/inviteUser.ts | 4 +- server/routers/user/removeUserOrg.ts | 4 +- server/routers/ws/index.ts | 26 +- server/routers/ws/messageHandlers.ts | 12 +- server/routers/ws/types.ts | 70 +++++ server/routers/ws/ws.ts | 56 +--- src/app/[orgId]/layout.tsx | 4 +- .../settings/(private)/billing/layout.tsx | 13 - .../settings/(private)/billing/page.tsx | 15 +- .../(private)/idp/[idpId]/general/page.tsx | 13 - .../settings/(private)/idp/[idpId]/layout.tsx | 13 - .../settings/(private)/idp/[idpId]/page.tsx | 13 - .../settings/(private)/idp/create/page.tsx | 13 - .../[orgId]/settings/(private)/idp/page.tsx | 17 +- .../remote-exit-nodes/ExitNodesDataTable.tsx | 13 - .../remote-exit-nodes/ExitNodesTable.tsx | 13 - .../[remoteExitNodeId]/general/page.tsx | 13 - .../[remoteExitNodeId]/layout.tsx | 17 +- .../[remoteExitNodeId]/page.tsx | 13 - .../remote-exit-nodes/create/page.tsx | 15 +- .../(private)/remote-exit-nodes/page.tsx | 15 +- .../settings/access/users/create/page.tsx | 6 +- .../[niceId]/authentication/page.tsx | 6 +- .../[orgId]/settings/sites/create/page.tsx | 2 +- src/app/auth/(private)/org/page.tsx | 24 +- .../auth/idp/[idpId]/oidc/callback/page.tsx | 2 +- src/app/auth/resource/[resourceGuid]/page.tsx | 8 +- src/app/layout.tsx | 2 +- src/components/DomainPicker.tsx | 2 +- src/components/private/AuthPageSettings.tsx | 21 +- .../private/AutoProvisionConfigWidget.tsx | 13 - src/components/private/CertificateStatus.tsx | 15 +- src/components/private/IdpLoginButtons.tsx | 13 - src/components/private/OrgIdpDataTable.tsx | 13 - src/components/private/OrgIdpTable.tsx | 13 - src/components/private/RegionSelector.tsx | 13 - src/components/private/SplashImage.tsx | 13 - .../private/ValidateSessionTransferToken.tsx | 15 +- src/contexts/privateRemoteExitNodeContext.ts | 24 -- .../privateSubscriptionStatusContext.ts | 28 -- src/contexts/remoteExitNodeContext.ts | 11 + src/contexts/subscriptionStatusContext.ts | 15 + src/hooks/privateUseRemoteExitNodeContext.ts | 29 -- .../privateUseSubscriptionStatusContext.ts | 29 -- ...ateUseCertificate.ts => useCertificate.ts} | 15 +- src/hooks/useRemoteExitNodeContext.ts | 16 ++ src/hooks/useSubscriptionStatusContext.ts | 16 ++ .../{privateThemeColors.ts => themeColors.ts} | 13 - ...rovider.tsx => RemoteExitNodeProvider.tsx} | 17 +- ...der.tsx => SubscriptionStatusProvider.tsx} | 23 +- ...DataProvider.tsx => ThemeDataProvider.tsx} | 15 +- tsconfig.json | 6 +- 224 files changed, 1507 insertions(+), 1586 deletions(-) create mode 100644 server/cleanup.ts rename server/emails/templates/{PrivateNotifyUsageLimitApproaching.tsx => NotifyUsageLimitApproaching.tsx} (86%) rename server/emails/templates/{PrivateNotifyUsageLimitReached.tsx => NotifyUsageLimitReached.tsx} (87%) create mode 100644 server/lib/billing/createCustomer.ts rename server/lib/{private => }/billing/features.ts (88%) create mode 100644 server/lib/billing/getOrgTierData.ts create mode 100644 server/lib/billing/index.ts rename server/lib/{private => }/billing/limitSet.ts (82%) rename server/lib/{private => }/billing/limitsService.ts (76%) rename server/lib/{private => }/billing/tiers.ts (69%) rename server/lib/{private => }/billing/usageService.ts (97%) rename server/lib/{remoteCertificates => }/certificates.ts (96%) rename server/{middlewares/private => lib}/corsWithLoginPage.ts (88%) rename server/lib/{private => }/createUserAccountOrg.ts (89%) delete mode 100644 server/lib/private/rateLimitStore.ts delete mode 100644 server/lib/private/s3.ts delete mode 100644 server/lib/remoteCertificates/index.ts create mode 100644 server/lib/resend.ts create mode 100644 server/lib/s3.ts rename server/{auth/sessions/privateRemoteExitNode.ts => private/auth/sessions/remoteExitNode.ts} (100%) create mode 100644 server/private/cleanup.ts rename server/{routers/private => private/lib}/billing/createCustomer.ts (96%) create mode 100644 server/private/lib/billing/getOrgTierData.ts rename server/{lib/private => private/lib}/billing/index.ts (81%) rename server/{lib/remoteCertificates/privateCertificates.ts => private/lib/certificates.ts} (97%) create mode 100644 server/private/lib/config.ts rename server/{lib/exitNodes/privateExitNodeComms.ts => private/lib/exitNodes/exitNodeComms.ts} (95%) rename server/{lib/exitNodes/privateExitNodes.ts => private/lib/exitNodes/exitNodes.ts} (100%) create mode 100644 server/private/lib/exitNodes/index.ts rename server/{db/private => private/lib}/rateLimit.test.ts (100%) rename server/{db/private => private/lib}/rateLimit.ts (98%) create mode 100644 server/private/lib/rateLimitStore.ts rename server/{lib/private => private/lib}/readConfigFile.ts (99%) rename server/{db/private => private/lib}/redis.ts (98%) rename server/{db/private => private/lib}/redisStore.ts (100%) rename server/{lib/private => private/lib}/resend.ts (96%) rename server/{lib/private => private/lib}/stripe.ts (84%) rename server/{lib/traefik/privateGetTraefikConfig.ts => private/lib/traefik/getTraefikConfig.ts} (99%) create mode 100644 server/private/lib/traefik/index.ts rename server/{middlewares/private => private/middlewares}/index.ts (92%) rename server/{middlewares/private => private/middlewares}/verifyCertificateAccess.ts (100%) rename server/{middlewares/private => private/middlewares}/verifyIdpAccess.ts (100%) rename server/{middlewares/private => private/middlewares}/verifyLoginPageAccess.ts (100%) rename server/{middlewares/private => private/middlewares}/verifyRemoteExitNode.ts (94%) rename server/{middlewares/private => private/middlewares}/verifyRemoteExitNodeAccess.ts (100%) rename server/{routers/auth/privateGetSessionTransferToken.ts => private/routers/auth/getSessionTransferToken.ts} (100%) create mode 100644 server/private/routers/auth/index.ts rename server/{routers/auth/privateQuickStart.ts => private/routers/auth/quickStart.ts} (98%) rename server/{routers/auth/privateTransferSession.ts => private/routers/auth/transferSession.ts} (100%) rename server/{routers/private => private/routers}/billing/createCheckoutSession.ts (95%) rename server/{routers/private => private/routers}/billing/createPortalSession.ts (98%) rename server/{routers/private => private/routers}/billing/getOrgSubscription.ts (100%) rename server/{routers/private => private/routers}/billing/getOrgUsage.ts (96%) rename server/{routers/private => private/routers}/billing/hooks/handleCustomerCreated.ts (100%) rename server/{routers/private => private/routers}/billing/hooks/handleCustomerDeleted.ts (100%) rename server/{routers/private => private/routers}/billing/hooks/handleCustomerUpdated.ts (100%) rename server/{routers/private => private/routers}/billing/hooks/handleSubscriptionCreated.ts (97%) rename server/{routers/private => private/routers}/billing/hooks/handleSubscriptionDeleted.ts (97%) rename server/{routers/private => private/routers}/billing/hooks/handleSubscriptionUpdated.ts (98%) rename server/{routers/private => private/routers}/billing/index.ts (100%) rename server/{routers/private => private/routers}/billing/internalGetOrgTier.ts (68%) rename server/{routers/private => private/routers}/billing/subscriptionLifecycle.ts (93%) rename server/{routers/private => private/routers}/billing/webhooks.ts (95%) rename server/{routers/private => private/routers}/certificates/createCertificate.ts (100%) rename server/{routers/private => private/routers}/certificates/getCertificate.ts (100%) rename server/{routers/private => private/routers}/certificates/index.ts (100%) rename server/{routers/private => private/routers}/certificates/restartCertificate.ts (100%) rename server/{routers/domain/privateCheckDomainNamespaceAvailability.ts => private/routers/domain/checkDomainNamespaceAvailability.ts} (100%) create mode 100644 server/private/routers/domain/index.ts rename server/{routers/domain/privateListDomainNamespaces.ts => private/routers/domain/listDomainNamespaces.ts} (100%) create mode 100644 server/private/routers/external.ts rename server/{routers/gerbil/privateCreateExitNode.ts => private/routers/gerbil/createExitNode.ts} (100%) rename server/{routers/gerbil/privateReceiveBandwidth.ts => private/routers/gerbil/receiveBandwidth.ts} (100%) rename server/{routers/private => private/routers}/hybrid.ts (98%) create mode 100644 server/private/routers/integration.ts create mode 100644 server/private/routers/internal.ts rename server/{routers/private => private/routers}/loginPage/createLoginPage.ts (96%) rename server/{routers/private => private/routers}/loginPage/deleteLoginPage.ts (100%) rename server/{routers/private => private/routers}/loginPage/getLoginPage.ts (100%) rename server/{routers/private => private/routers}/loginPage/index.ts (100%) rename server/{routers/private => private/routers}/loginPage/loadLoginPage.ts (100%) rename server/{routers/private => private/routers}/loginPage/updateLoginPage.ts (96%) create mode 100644 server/private/routers/org/index.ts rename server/{routers/org/privateSendUsageNotifications.ts => private/routers/org/sendUsageNotifications.ts} (98%) rename server/{routers/private => private/routers}/orgIdp/createOrgOidcIdp.ts (97%) create mode 100644 server/private/routers/orgIdp/deleteOrgIdp.ts rename server/{routers/private => private/routers}/orgIdp/getOrgIdp.ts (100%) rename server/{routers/private => private/routers}/orgIdp/index.ts (87%) rename server/{routers/private => private/routers}/orgIdp/listOrgIdps.ts (100%) rename server/{routers/private => private/routers}/orgIdp/updateOrgOidcIdp.ts (97%) rename server/{routers/private => private/routers}/remoteExitNode/createRemoteExitNode.ts (97%) rename server/{routers/private => private/routers}/remoteExitNode/deleteRemoteExitNode.ts (96%) rename server/{routers/private => private/routers}/remoteExitNode/getRemoteExitNode.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/getRemoteExitNodeToken.ts (98%) rename server/{routers/private => private/routers}/remoteExitNode/handleRemoteExitNodePingMessage.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/index.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/listRemoteExitNodes.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/pickRemoteExitNodeDefaults.ts (100%) rename server/{routers/private => private/routers}/remoteExitNode/quickStartRemoteExitNode.ts (100%) rename src/lib/types/privateThemeTypes.tsx => server/private/routers/ws/index.ts (94%) create mode 100644 server/private/routers/ws/messageHandlers.ts rename server/{routers/ws/privateWs.ts => private/routers/ws/ws.ts} (94%) create mode 100644 server/routers/billing/webhooks.ts create mode 100644 server/routers/certificates/createCertificate.ts create mode 100644 server/routers/hybrid.ts create mode 100644 server/routers/ws/types.ts delete mode 100644 src/contexts/privateRemoteExitNodeContext.ts delete mode 100644 src/contexts/privateSubscriptionStatusContext.ts create mode 100644 src/contexts/remoteExitNodeContext.ts create mode 100644 src/contexts/subscriptionStatusContext.ts delete mode 100644 src/hooks/privateUseRemoteExitNodeContext.ts delete mode 100644 src/hooks/privateUseSubscriptionStatusContext.ts rename src/hooks/{privateUseCertificate.ts => useCertificate.ts} (88%) create mode 100644 src/hooks/useRemoteExitNodeContext.ts create mode 100644 src/hooks/useSubscriptionStatusContext.ts rename src/lib/{privateThemeColors.ts => themeColors.ts} (87%) rename src/providers/{PrivateRemoteExitNodeProvider.tsx => RemoteExitNodeProvider.tsx} (67%) rename src/providers/{PrivateSubscriptionStatusProvider.tsx => SubscriptionStatusProvider.tsx} (72%) rename src/providers/{PrivateThemeDataProvider.tsx => ThemeDataProvider.tsx} (67%) diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index 2b10d2af..8fc99161 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -2,15 +2,10 @@ import { defineConfig } from "drizzle-kit"; import path from "path"; import { build } from "@server/build"; -let schema; -if (build === "oss") { - schema = [path.join("server", "db", "pg", "schema.ts")]; -} else { - schema = [ - path.join("server", "db", "pg", "schema.ts"), - path.join("server", "db", "pg", "privateSchema.ts") - ]; -} +const schema = [ + path.join("server", "db", "pg", "schema.ts"), + path.join("server", "db", "pg", "pSchema.ts") +]; export default defineConfig({ dialect: "postgresql", diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index 25bbe7f3..b8679aa9 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -3,15 +3,10 @@ import { APP_PATH } from "@server/lib/consts"; import { defineConfig } from "drizzle-kit"; import path from "path"; -let schema; -if (build === "oss") { - schema = [path.join("server", "db", "sqlite", "schema.ts")]; -} else { - schema = [ - path.join("server", "db", "sqlite", "schema.ts"), - path.join("server", "db", "sqlite", "privateSchema.ts") - ]; -} +const schema = [ + path.join("server", "db", "sqlite", "schema.ts"), + path.join("server", "db", "sqlite", "pSchema.ts") +]; export default defineConfig({ dialect: "sqlite", diff --git a/esbuild.mjs b/esbuild.mjs index 8086a77e..7f67fe81 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -2,8 +2,9 @@ import esbuild from "esbuild"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { nodeExternalsPlugin } from "esbuild-node-externals"; +import path from "path"; +import fs from "fs"; // import { glob } from "glob"; -// import path from "path"; const banner = ` // patch __dirname @@ -18,7 +19,7 @@ const require = topLevelCreateRequire(import.meta.url); `; const argv = yargs(hideBin(process.argv)) - .usage("Usage: $0 -entry [string] -out [string]") + .usage("Usage: $0 -entry [string] -out [string] -build [string]") .option("entry", { alias: "e", describe: "Entry point file", @@ -31,6 +32,13 @@ const argv = yargs(hideBin(process.argv)) type: "string", demandOption: true, }) + .option("build", { + alias: "b", + describe: "Build type (oss, saas, enterprise)", + type: "string", + choices: ["oss", "saas", "enterprise"], + default: "oss", + }) .help() .alias("help", "h").argv; @@ -46,6 +54,179 @@ function getPackagePaths() { return ["package.json"]; } +// Plugin to guard against bad imports from #private +function privateImportGuardPlugin() { + return { + name: "private-import-guard", + setup(build) { + const violations = []; + + build.onResolve({ filter: /^#private\// }, (args) => { + const importingFile = args.importer; + + // Check if the importing file is NOT in server/private + const normalizedImporter = path.normalize(importingFile); + const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + + if (!isInServerPrivate) { + const violation = { + file: importingFile, + importPath: args.path, + resolveDir: args.resolveDir + }; + violations.push(violation); + + console.log(`PRIVATE IMPORT VIOLATION:`); + console.log(` File: ${importingFile}`); + console.log(` Import: ${args.path}`); + console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); + console.log(''); + } + + // Return null to let the default resolver handle it + return null; + }); + + build.onEnd((result) => { + if (violations.length > 0) { + console.log(`\nSUMMARY: Found ${violations.length} private import violation(s):`); + violations.forEach((v, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + }); + console.log(''); + + result.errors.push({ + text: `Private import violations detected: ${violations.length} violation(s) found`, + location: null, + notes: violations.map(v => ({ + text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, + location: null + })) + }); + } + }); + } + }; +} + +// Plugin to guard against bad imports from #private +function dynamicImportGuardPlugin() { + return { + name: "dynamic-import-guard", + setup(build) { + const violations = []; + + build.onResolve({ filter: /^#dynamic\// }, (args) => { + const importingFile = args.importer; + + // Check if the importing file is NOT in server/private + const normalizedImporter = path.normalize(importingFile); + const isInServerPrivate = normalizedImporter.includes(path.normalize("server/private")); + + if (isInServerPrivate) { + const violation = { + file: importingFile, + importPath: args.path, + resolveDir: args.resolveDir + }; + violations.push(violation); + + console.log(`DYNAMIC IMPORT VIOLATION:`); + console.log(` File: ${importingFile}`); + console.log(` Import: ${args.path}`); + console.log(` Resolve dir: ${args.resolveDir || 'N/A'}`); + console.log(''); + } + + // Return null to let the default resolver handle it + return null; + }); + + build.onEnd((result) => { + if (violations.length > 0) { + console.log(`\nSUMMARY: Found ${violations.length} dynamic import violation(s):`); + violations.forEach((v, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`); + }); + console.log(''); + + result.errors.push({ + text: `Dynamic import violations detected: ${violations.length} violation(s) found`, + location: null, + notes: violations.map(v => ({ + text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`, + location: null + })) + }); + } + }); + } + }; +} + +// Plugin to dynamically switch imports based on build type +function dynamicImportSwitcherPlugin(buildValue) { + return { + name: "dynamic-import-switcher", + setup(build) { + const switches = []; + + build.onStart(() => { + console.log(`Dynamic import switcher using build type: ${buildValue}`); + }); + + build.onResolve({ filter: /^#dynamic\// }, (args) => { + // Extract the path after #dynamic/ + const dynamicPath = args.path.replace(/^#dynamic\//, ''); + + // Determine the replacement based on build type + let replacement; + if (buildValue === "oss") { + replacement = `#open/${dynamicPath}`; + } else if (buildValue === "saas" || buildValue === "enterprise") { + replacement = `#closed/${dynamicPath}`; // We use #closed here so that the route guards dont complain after its been changed but this is the same as #private + } else { + console.warn(`Unknown build type '${buildValue}', defaulting to #open/`); + replacement = `#open/${dynamicPath}`; + } + + const switchInfo = { + file: args.importer, + originalPath: args.path, + replacementPath: replacement, + buildType: buildValue + }; + switches.push(switchInfo); + + console.log(`DYNAMIC IMPORT SWITCH:`); + console.log(` File: ${args.importer}`); + console.log(` Original: ${args.path}`); + console.log(` Switched to: ${replacement} (build: ${buildValue})`); + console.log(''); + + // Rewrite the import path and let the normal resolution continue + return build.resolve(replacement, { + importer: args.importer, + namespace: args.namespace, + resolveDir: args.resolveDir, + kind: args.kind + }); + }); + + build.onEnd((result) => { + if (switches.length > 0) { + console.log(`\nDYNAMIC IMPORT SUMMARY: Switched ${switches.length} import(s) for build type '${buildValue}':`); + switches.forEach((s, i) => { + console.log(` ${i + 1}. ${path.relative(process.cwd(), s.file)}`); + console.log(` ${s.originalPath} → ${s.replacementPath}`); + }); + console.log(''); + } + }); + } + }; +} + esbuild .build({ entryPoints: [argv.entry], @@ -59,6 +240,9 @@ esbuild platform: "node", external: ["body-parser"], plugins: [ + privateImportGuardPlugin(), + dynamicImportGuardPlugin(), + dynamicImportSwitcherPlugin(argv.build), nodeExternalsPlugin({ packagePath: getPackagePaths(), }), @@ -66,7 +250,27 @@ esbuild sourcemap: "inline", target: "node22", }) - .then(() => { + .then((result) => { + // Check if there were any errors in the build result + if (result.errors && result.errors.length > 0) { + console.error(`Build failed with ${result.errors.length} error(s):`); + result.errors.forEach((error, i) => { + console.error(`${i + 1}. ${error.text}`); + if (error.notes) { + error.notes.forEach(note => { + console.error(` - ${note.text}`); + }); + } + }); + + // remove the output file if it was created + if (fs.existsSync(argv.out)) { + fs.unlinkSync(argv.out); + } + + process.exit(1); + } + console.log("Build completed successfully"); }) .catch((error) => { diff --git a/server/apiServer.ts b/server/apiServer.ts index 0b5a6305..9a626769 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -7,21 +7,21 @@ import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares"; -import { corsWithLoginPageSupport } from "@server/middlewares/private/corsWithLoginPage"; -import { authenticated, unauthenticated } from "@server/routers/external"; -import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws"; +import { authenticated, unauthenticated } from "#dynamic/routers/external"; +import { router as wsRouter, handleWSUpgrade } from "#dynamic/routers/ws"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; import { csrfProtectionMiddleware } from "./middlewares/csrfProtection"; import helmet from "helmet"; -import { stripeWebhookHandler } from "@server/routers/private/billing/webhooks"; import { build } from "./build"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import HttpCode from "./types/HttpCode"; import requestTimeoutMiddleware from "./middlewares/requestTimeout"; -import { createStore } from "@server/lib/private/rateLimitStore"; -import hybridRouter from "@server/routers/private/hybrid"; +import { createStore } from "#dynamic/lib/rateLimitStore"; import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; +import { corsWithLoginPageSupport } from "@server/lib/corsWithLoginPage"; +import { hybridRouter } from "#dynamic/routers/hybrid"; +import { billingWebhookHandler } from "#dynamic/routers/billing/webhooks"; const dev = config.isDev; const externalPort = config.getRawConfig().server.external_port; @@ -39,7 +39,7 @@ export function createApiServer() { apiServer.post( `${prefix}/billing/webhooks`, express.raw({ type: "application/json" }), - stripeWebhookHandler + billingWebhookHandler ); } diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 668be0db..e48bc502 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -4,7 +4,6 @@ import { userActions, roleActions, userOrgs } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { sendUsageNotification } from "@server/routers/org"; export enum ActionsEnum { createOrgUser = "createOrgUser", diff --git a/server/cleanup.ts b/server/cleanup.ts new file mode 100644 index 00000000..de54ed77 --- /dev/null +++ b/server/cleanup.ts @@ -0,0 +1,13 @@ +import { cleanup as wsCleanup } from "@server/routers/ws"; + +async function cleanup() { + await wsCleanup(); + + process.exit(0); +} + +export async function initCleanup() { + // Handle process termination + process.on("SIGTERM", () => cleanup()); + process.on("SIGINT", () => cleanup()); +} \ No newline at end of file diff --git a/server/db/pg/privateSchema.ts b/server/db/pg/privateSchema.ts index 8ea8f9de..67fb28ec 100644 --- a/server/db/pg/privateSchema.ts +++ b/server/db/pg/privateSchema.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { pgTable, serial, diff --git a/server/db/sqlite/privateSchema.ts b/server/db/sqlite/privateSchema.ts index fbe86e25..557ebfd6 100644 --- a/server/db/sqlite/privateSchema.ts +++ b/server/db/sqlite/privateSchema.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { sqliteTable, integer, diff --git a/server/emails/sendEmail.ts b/server/emails/sendEmail.ts index 24fb4fbd..c8a0b077 100644 --- a/server/emails/sendEmail.ts +++ b/server/emails/sendEmail.ts @@ -2,7 +2,6 @@ import { render } from "@react-email/render"; import { ReactElement } from "react"; import emailClient from "@server/emails"; import logger from "@server/logger"; -import config from "@server/lib/config"; export async function sendEmail( template: ReactElement, @@ -25,7 +24,7 @@ export async function sendEmail( const emailHtml = await render(template); - const appName = config.getRawPrivateConfig().branding?.app_name || "Pangolin"; + const appName = process.env.BRANDING_APP_NAME || "Pangolin"; // From the private config loading into env vars to seperate away the private config await emailClient.sendMail({ from: { diff --git a/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx b/server/emails/templates/NotifyUsageLimitApproaching.tsx similarity index 86% rename from server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx rename to server/emails/templates/NotifyUsageLimitApproaching.tsx index c66265e5..beab0300 100644 --- a/server/emails/templates/PrivateNotifyUsageLimitApproaching.tsx +++ b/server/emails/templates/NotifyUsageLimitApproaching.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import React from "react"; import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; import { themeColors } from "./lib/theme"; diff --git a/server/emails/templates/PrivateNotifyUsageLimitReached.tsx b/server/emails/templates/NotifyUsageLimitReached.tsx similarity index 87% rename from server/emails/templates/PrivateNotifyUsageLimitReached.tsx rename to server/emails/templates/NotifyUsageLimitReached.tsx index c4eac322..783d1b0e 100644 --- a/server/emails/templates/PrivateNotifyUsageLimitReached.tsx +++ b/server/emails/templates/NotifyUsageLimitReached.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import React from "react"; import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; import { themeColors } from "./lib/theme"; diff --git a/server/index.ts b/server/index.ts index 2497e301..f4571422 100644 --- a/server/index.ts +++ b/server/index.ts @@ -12,6 +12,7 @@ import config from "@server/lib/config"; import { setHostMeta } from "@server/lib/hostMeta"; import { initTelemetryClient } from "./lib/telemetry.js"; import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager.js"; +import { initCleanup } from "#dynamic/cleanup"; async function startServers() { await setHostMeta(); @@ -42,6 +43,8 @@ async function startServers() { integrationServer = createIntegrationApiServer(); } + await initCleanup(); + return { apiServer, nextServer, diff --git a/server/integrationApiServer.ts b/server/integrationApiServer.ts index cbbea83f..3416004c 100644 --- a/server/integrationApiServer.ts +++ b/server/integrationApiServer.ts @@ -7,7 +7,7 @@ import { errorHandlerMiddleware, notFoundMiddleware, } from "@server/middlewares"; -import { authenticated, unauthenticated } from "@server/routers/integration"; +import { authenticated, unauthenticated } from "#dynamic/routers/integration"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; import helmet from "helmet"; import swaggerUi from "swagger-ui-express"; diff --git a/server/internalServer.ts b/server/internalServer.ts index 0ba64eec..d15e3c45 100644 --- a/server/internalServer.ts +++ b/server/internalServer.ts @@ -8,7 +8,7 @@ import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares"; -import internal from "@server/routers/internal"; +import { internalRouter } from "#dynamic/routers/internal"; import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions"; const internalPort = config.getRawConfig().server.internal_port; @@ -23,7 +23,7 @@ export function createInternalServer() { internalServer.use(express.json()); const prefix = `/api/v1`; - internalServer.use(prefix, internal); + internalServer.use(prefix, internalRouter); internalServer.use(notFoundMiddleware); internalServer.use(errorHandlerMiddleware); diff --git a/server/lib/billing/createCustomer.ts b/server/lib/billing/createCustomer.ts new file mode 100644 index 00000000..7f65bfb2 --- /dev/null +++ b/server/lib/billing/createCustomer.ts @@ -0,0 +1,6 @@ +export async function createCustomer( + orgId: string, + email: string | null | undefined +): Promise { + return; +} diff --git a/server/lib/private/billing/features.ts b/server/lib/billing/features.ts similarity index 88% rename from server/lib/private/billing/features.ts rename to server/lib/billing/features.ts index 11d78bbb..b72543cc 100644 --- a/server/lib/private/billing/features.ts +++ b/server/lib/billing/features.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import Stripe from "stripe"; export enum FeatureId { diff --git a/server/lib/billing/getOrgTierData.ts b/server/lib/billing/getOrgTierData.ts new file mode 100644 index 00000000..24664790 --- /dev/null +++ b/server/lib/billing/getOrgTierData.ts @@ -0,0 +1,8 @@ +export async function getOrgTierData( + orgId: string +): Promise<{ tier: string | null; active: boolean }> { + let tier = null; + let active = false; + + return { tier, active }; +} diff --git a/server/lib/billing/index.ts b/server/lib/billing/index.ts new file mode 100644 index 00000000..6c3ef792 --- /dev/null +++ b/server/lib/billing/index.ts @@ -0,0 +1,5 @@ +export * from "./limitSet"; +export * from "./features"; +export * from "./limitsService"; +export * from "./getOrgTierData"; +export * from "./createCustomer"; \ No newline at end of file diff --git a/server/lib/private/billing/limitSet.ts b/server/lib/billing/limitSet.ts similarity index 82% rename from server/lib/private/billing/limitSet.ts rename to server/lib/billing/limitSet.ts index ec6107b2..153d8ae8 100644 --- a/server/lib/private/billing/limitSet.ts +++ b/server/lib/billing/limitSet.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { FeatureId } from "./features"; export type LimitSet = { diff --git a/server/lib/private/billing/limitsService.ts b/server/lib/billing/limitsService.ts similarity index 76% rename from server/lib/private/billing/limitsService.ts rename to server/lib/billing/limitsService.ts index 168f5580..a07f70b3 100644 --- a/server/lib/private/billing/limitsService.ts +++ b/server/lib/billing/limitsService.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { db, limits } from "@server/db"; import { and, eq } from "drizzle-orm"; import { LimitSet } from "./limitSet"; diff --git a/server/lib/private/billing/tiers.ts b/server/lib/billing/tiers.ts similarity index 69% rename from server/lib/private/billing/tiers.ts rename to server/lib/billing/tiers.ts index e6322c9f..6ccf8898 100644 --- a/server/lib/private/billing/tiers.ts +++ b/server/lib/billing/tiers.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - export enum TierId { STANDARD = "standard", } diff --git a/server/lib/private/billing/usageService.ts b/server/lib/billing/usageService.ts similarity index 97% rename from server/lib/private/billing/usageService.ts rename to server/lib/billing/usageService.ts index f18542d2..edff41f0 100644 --- a/server/lib/private/billing/usageService.ts +++ b/server/lib/billing/usageService.ts @@ -1,21 +1,7 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { eq, sql, and } from "drizzle-orm"; import NodeCache from "node-cache"; import { v4 as uuidv4 } from "uuid"; import { PutObjectCommand } from "@aws-sdk/client-s3"; -import { s3Client } from "../s3"; import * as fs from "fs/promises"; import * as path from "path"; import { @@ -30,10 +16,10 @@ import { Transaction } from "@server/db"; import { FeatureId, getFeatureMeterId } from "./features"; -import config from "@server/lib/config"; import logger from "@server/logger"; -import { sendToClient } from "@server/routers/ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { build } from "@server/build"; +import { s3Client } from "@server/lib/s3"; interface StripeEvent { identifier?: string; @@ -58,8 +44,10 @@ export class UsageService { if (build !== "saas") { return; } - this.bucketName = config.getRawPrivateConfig().stripe?.s3Bucket; - this.eventsDir = config.getRawPrivateConfig().stripe?.localFilePath; + // this.bucketName = privateConfig.getRawPrivateConfig().stripe?.s3Bucket; + // this.eventsDir = privateConfig.getRawPrivateConfig().stripe?.localFilePath; + this.bucketName = process.env.S3_BUCKET || undefined; + this.eventsDir = process.env.LOCAL_FILE_PATH || undefined; // Ensure events directory exists this.initializeEventsDirectory().then(() => { diff --git a/server/lib/blueprints/applyNewtDockerBlueprint.ts b/server/lib/blueprints/applyNewtDockerBlueprint.ts index f69e4854..2afba84c 100644 --- a/server/lib/blueprints/applyNewtDockerBlueprint.ts +++ b/server/lib/blueprints/applyNewtDockerBlueprint.ts @@ -1,4 +1,4 @@ -import { sendToClient } from "@server/routers/ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { processContainerLabels } from "./parseDockerContainers"; import { applyBlueprint } from "./applyBlueprint"; import { db, sites } from "@server/db"; diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index 407c6019..a31cfb9d 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -25,7 +25,7 @@ import { TargetData } from "./types"; import logger from "@server/logger"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { pickPort } from "@server/routers/target/helpers"; import { resourcePassword } from "@server/db"; import { hashPassword } from "@server/auth/password"; diff --git a/server/lib/remoteCertificates/certificates.ts b/server/lib/certificates.ts similarity index 96% rename from server/lib/remoteCertificates/certificates.ts rename to server/lib/certificates.ts index 6404ee75..4032d1ec 100644 --- a/server/lib/remoteCertificates/certificates.ts +++ b/server/lib/certificates.ts @@ -1,7 +1,7 @@ import axios from "axios"; -import { tokenManager } from "../tokenManager"; +import { tokenManager } from "./tokenManager"; import logger from "@server/logger"; -import config from "../config"; +import config from "./config"; /** * Get valid certificates for the specified domains diff --git a/server/lib/config.ts b/server/lib/config.ts index 8b084e62..103ea5ae 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -6,16 +6,10 @@ import { eq } from "drizzle-orm"; import { license } from "@server/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; import { fromError } from "zod-validation-error"; -import { - privateConfigSchema, - readPrivateConfigFile -} from "@server/lib/private/readConfigFile"; -import logger from "@server/logger"; import { build } from "@server/build"; export class Config { private rawConfig!: z.infer; - private rawPrivateConfig!: z.infer; supporterData: SupporterKey | null = null; @@ -37,19 +31,6 @@ export class Config { throw new Error(`Invalid configuration file: ${errors}`); } - const privateEnvironment = readPrivateConfigFile(); - - const { - data: parsedPrivateConfig, - success: privateSuccess, - error: privateError - } = privateConfigSchema.safeParse(privateEnvironment); - - if (!privateSuccess) { - const errors = fromError(privateError); - throw new Error(`Invalid private configuration file: ${errors}`); - } - if ( // @ts-ignore parsedConfig.users || @@ -109,110 +90,11 @@ export class Config { ? "true" : "false"; - if (parsedPrivateConfig.branding?.colors) { - process.env.BRANDING_COLORS = JSON.stringify( - parsedPrivateConfig.branding?.colors - ); - } - - if (parsedPrivateConfig.branding?.logo?.light_path) { - process.env.BRANDING_LOGO_LIGHT_PATH = - parsedPrivateConfig.branding?.logo?.light_path; - } - if (parsedPrivateConfig.branding?.logo?.dark_path) { - process.env.BRANDING_LOGO_DARK_PATH = - parsedPrivateConfig.branding?.logo?.dark_path || undefined; - } - - process.env.HIDE_SUPPORTER_KEY = parsedPrivateConfig.flags - ?.hide_supporter_key - ? "true" - : "false"; - - if (build != "oss") { - if (parsedPrivateConfig.branding?.logo?.light_path) { - process.env.BRANDING_LOGO_LIGHT_PATH = - parsedPrivateConfig.branding?.logo?.light_path; - } - if (parsedPrivateConfig.branding?.logo?.dark_path) { - process.env.BRANDING_LOGO_DARK_PATH = - parsedPrivateConfig.branding?.logo?.dark_path || undefined; - } - - process.env.BRANDING_LOGO_AUTH_WIDTH = parsedPrivateConfig.branding - ?.logo?.auth_page?.width - ? parsedPrivateConfig.branding?.logo?.auth_page?.width.toString() - : undefined; - process.env.BRANDING_LOGO_AUTH_HEIGHT = parsedPrivateConfig.branding - ?.logo?.auth_page?.height - ? parsedPrivateConfig.branding?.logo?.auth_page?.height.toString() - : undefined; - - process.env.BRANDING_LOGO_NAVBAR_WIDTH = parsedPrivateConfig - .branding?.logo?.navbar?.width - ? parsedPrivateConfig.branding?.logo?.navbar?.width.toString() - : undefined; - process.env.BRANDING_LOGO_NAVBAR_HEIGHT = parsedPrivateConfig - .branding?.logo?.navbar?.height - ? parsedPrivateConfig.branding?.logo?.navbar?.height.toString() - : undefined; - - process.env.BRANDING_FAVICON_PATH = - parsedPrivateConfig.branding?.favicon_path; - - process.env.BRANDING_APP_NAME = - parsedPrivateConfig.branding?.app_name || "Pangolin"; - - if (parsedPrivateConfig.branding?.footer) { - process.env.BRANDING_FOOTER = JSON.stringify( - parsedPrivateConfig.branding?.footer - ); - } - - process.env.LOGIN_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.login_page?.title_text || ""; - process.env.LOGIN_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.login_page?.subtitle_text || ""; - - process.env.SIGNUP_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.signup_page?.title_text || ""; - process.env.SIGNUP_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.signup_page?.subtitle_text || ""; - - process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = - parsedPrivateConfig.branding?.resource_auth_page - ?.hide_powered_by === true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = - parsedPrivateConfig.branding?.resource_auth_page?.show_logo === - true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = - parsedPrivateConfig.branding?.resource_auth_page?.title_text || - ""; - process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = - parsedPrivateConfig.branding?.resource_auth_page - ?.subtitle_text || ""; - - if (parsedPrivateConfig.branding?.background_image_path) { - process.env.BACKGROUND_IMAGE_PATH = - parsedPrivateConfig.branding?.background_image_path; - } - - if (parsedPrivateConfig.server.reo_client_id) { - process.env.REO_CLIENT_ID = - parsedPrivateConfig.server.reo_client_id; - } - } - if (parsedConfig.server.maxmind_db_path) { process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path; } this.rawConfig = parsedConfig; - this.rawPrivateConfig = parsedPrivateConfig; } public async initServer() { @@ -231,7 +113,6 @@ export class Config { private async checkKeyStatus() { const licenseStatus = await license.check(); if ( - !this.rawPrivateConfig.flags?.hide_supporter_key && build != "oss" && !licenseStatus.isHostLicensed ) { @@ -243,10 +124,6 @@ export class Config { return this.rawConfig; } - public getRawPrivateConfig() { - return this.rawPrivateConfig; - } - public getNoReplyEmail(): string | undefined { return ( this.rawConfig.email?.no_reply || this.rawConfig.email?.smtp_user diff --git a/server/middlewares/private/corsWithLoginPage.ts b/server/lib/corsWithLoginPage.ts similarity index 88% rename from server/middlewares/private/corsWithLoginPage.ts rename to server/lib/corsWithLoginPage.ts index 95867fa1..43b26264 100644 --- a/server/middlewares/private/corsWithLoginPage.ts +++ b/server/lib/corsWithLoginPage.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { Request, Response, NextFunction } from "express"; import cors, { CorsOptions } from "cors"; import config from "@server/lib/config"; diff --git a/server/lib/private/createUserAccountOrg.ts b/server/lib/createUserAccountOrg.ts similarity index 89% rename from server/lib/private/createUserAccountOrg.ts rename to server/lib/createUserAccountOrg.ts index abde5ca7..8677d8e3 100644 --- a/server/lib/private/createUserAccountOrg.ts +++ b/server/lib/createUserAccountOrg.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { isValidCIDR } from "@server/lib/validators"; import { getNextAvailableOrgSubnet } from "@server/lib/ip"; import { @@ -28,9 +15,9 @@ import { } from "@server/db"; import { eq } from "drizzle-orm"; import { defaultRoleAllowedActions } from "@server/routers/role"; -import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/private/billing"; -import { createCustomer } from "@server/routers/private/billing/createCustomer"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/billing"; +import { createCustomer } from "@server/private/lib/billing/createCustomer"; +import { usageService } from "@server/lib/billing/usageService"; export async function createUserAccountOrg( userId: string, diff --git a/server/lib/exitNodes/exitNodes.ts b/server/lib/exitNodes/exitNodes.ts index 8372d675..bb269710 100644 --- a/server/lib/exitNodes/exitNodes.ts +++ b/server/lib/exitNodes/exitNodes.ts @@ -16,7 +16,11 @@ export async function verifyExitNodeOrgAccess( return { hasAccess: true, exitNode }; } -export async function listExitNodes(orgId: string, filterOnline = false, noCloud = false) { +export async function listExitNodes( + orgId: string, + filterOnline = false, + noCloud = false +) { // TODO: pick which nodes to send and ping better than just all of them that are not remote const allExitNodes = await db .select({ @@ -59,7 +63,16 @@ export async function checkExitNodeOrg(exitNodeId: number, orgId: string) { return false; } -export async function resolveExitNodes(hostname: string, publicKey: string) { +export async function resolveExitNodes( + hostname: string, + publicKey: string +): Promise< + { + endpoint: string; + publicKey: string; + orgId: string; + }[] +> { // OSS version: simple implementation that returns empty array return []; } diff --git a/server/lib/exitNodes/index.ts b/server/lib/exitNodes/index.ts index dda94368..ba30ccc2 100644 --- a/server/lib/exitNodes/index.ts +++ b/server/lib/exitNodes/index.ts @@ -1,33 +1,4 @@ -import { build } from "@server/build"; - -// Import both modules -import * as exitNodesModule from "./exitNodes"; -import * as privateExitNodesModule from "./privateExitNodes"; - -// Conditionally export exit nodes implementation based on build type -const exitNodesImplementation = build === "oss" ? exitNodesModule : privateExitNodesModule; - -// Re-export all items from the selected implementation -export const { - verifyExitNodeOrgAccess, - listExitNodes, - selectBestExitNode, - checkExitNodeOrg, - resolveExitNodes -} = exitNodesImplementation; - -// Import communications modules -import * as exitNodeCommsModule from "./exitNodeComms"; -import * as privateExitNodeCommsModule from "./privateExitNodeComms"; - -// Conditionally export communications implementation based on build type -const exitNodeCommsImplementation = build === "oss" ? exitNodeCommsModule : privateExitNodeCommsModule; - -// Re-export communications functions from the selected implementation -export const { - sendToExitNode -} = exitNodeCommsImplementation; - -// Re-export shared modules +export * from "./exitNodes"; +export * from "./exitNodeComms"; export * from "./subnet"; export * from "./getCurrentExitNodeId"; \ No newline at end of file diff --git a/server/lib/private/rateLimitStore.ts b/server/lib/private/rateLimitStore.ts deleted file mode 100644 index 4700ba09..00000000 --- a/server/lib/private/rateLimitStore.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import RedisStore from "@server/db/private/redisStore"; -import { MemoryStore, Store } from "express-rate-limit"; - -export function createStore(): Store { - const rateLimitStore: Store = new RedisStore({ - prefix: 'api-rate-limit', // Optional: customize Redis key prefix - skipFailedRequests: true, // Don't count failed requests - skipSuccessfulRequests: false, // Count successful requests - }); - - return rateLimitStore; -} diff --git a/server/lib/private/s3.ts b/server/lib/private/s3.ts deleted file mode 100644 index 26b1d49b..00000000 --- a/server/lib/private/s3.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { S3Client } from "@aws-sdk/client-s3"; -import config from "@server/lib/config"; - -export const s3Client = new S3Client({ - region: config.getRawPrivateConfig().stripe?.s3Region || "us-east-1", -}); diff --git a/server/lib/remoteCertificates/index.ts b/server/lib/remoteCertificates/index.ts deleted file mode 100644 index fcd43d30..00000000 --- a/server/lib/remoteCertificates/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { build } from "@server/build"; - -// Import both modules -import * as certificateModule from "./certificates"; -import * as privateCertificateModule from "./privateCertificates"; - -// Conditionally export Remote Certificates implementation based on build type -const remoteCertificatesImplementation = build === "oss" ? certificateModule : privateCertificateModule; - -// Re-export all items from the selected implementation -export const { - getValidCertificatesForDomains, - getValidCertificatesForDomainsHybrid - } = remoteCertificatesImplementation; \ No newline at end of file diff --git a/server/lib/resend.ts b/server/lib/resend.ts new file mode 100644 index 00000000..931dca20 --- /dev/null +++ b/server/lib/resend.ts @@ -0,0 +1,15 @@ +export enum AudienceIds { + General = "", + Subscribed = "", + Churned = "" +} + +let resend; +export default resend; + +export async function moveEmailToAudience( + email: string, + audienceId: AudienceIds +) { + return +} \ No newline at end of file diff --git a/server/lib/s3.ts b/server/lib/s3.ts new file mode 100644 index 00000000..5fc3318f --- /dev/null +++ b/server/lib/s3.ts @@ -0,0 +1,5 @@ +import { S3Client } from "@aws-sdk/client-s3"; + +export const s3Client = new S3Client({ + region: process.env.S3_REGION || "us-east-1", +}); diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 51466ecf..435a749f 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -8,12 +8,12 @@ import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; import { tokenManager } from "../tokenManager"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; -import { getTraefikConfig } from "./"; +import { getTraefikConfig } from "#dynamic/lib/traefik"; import { getValidCertificatesForDomains, getValidCertificatesForDomainsHybrid -} from "../remoteCertificates"; -import { sendToExitNode } from "../exitNodes"; +} from "#dynamic/lib/certificates"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; export class TraefikConfigManager { diff --git a/server/lib/traefik/index.ts b/server/lib/traefik/index.ts index 1d654510..5630028c 100644 --- a/server/lib/traefik/index.ts +++ b/server/lib/traefik/index.ts @@ -1,11 +1 @@ -import { build } from "@server/build"; - -// Import both modules -import * as traefikModule from "./getTraefikConfig"; -import * as privateTraefikModule from "./privateGetTraefikConfig"; - -// Conditionally export Traefik configuration implementation based on build type -const traefikImplementation = build === "oss" ? traefikModule : privateTraefikModule; - -// Re-export all items from the selected implementation -export const { getTraefikConfig } = traefikImplementation; \ No newline at end of file +export * from "./getTraefikConfig"; \ No newline at end of file diff --git a/server/auth/sessions/privateRemoteExitNode.ts b/server/private/auth/sessions/remoteExitNode.ts similarity index 100% rename from server/auth/sessions/privateRemoteExitNode.ts rename to server/private/auth/sessions/remoteExitNode.ts diff --git a/server/private/cleanup.ts b/server/private/cleanup.ts new file mode 100644 index 00000000..8e57ce29 --- /dev/null +++ b/server/private/cleanup.ts @@ -0,0 +1,15 @@ +import { rateLimitService } from "#private/lib/rateLimit"; +import { cleanup as wsCleanup } from "#private/routers/ws"; + +async function cleanup() { + await rateLimitService.cleanup(); + await wsCleanup(); + + process.exit(0); +} + +export async function initCleanup() { + // Handle process termination + process.on("SIGTERM", () => cleanup()); + process.on("SIGINT", () => cleanup()); +} \ No newline at end of file diff --git a/server/routers/private/billing/createCustomer.ts b/server/private/lib/billing/createCustomer.ts similarity index 96% rename from server/routers/private/billing/createCustomer.ts rename to server/private/lib/billing/createCustomer.ts index d1c08a0e..52c72c53 100644 --- a/server/routers/private/billing/createCustomer.ts +++ b/server/private/lib/billing/createCustomer.ts @@ -13,7 +13,7 @@ import { customers, db } from "@server/db"; import { eq } from "drizzle-orm"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; import { build } from "@server/build"; export async function createCustomer( diff --git a/server/private/lib/billing/getOrgTierData.ts b/server/private/lib/billing/getOrgTierData.ts new file mode 100644 index 00000000..fbfb5cb0 --- /dev/null +++ b/server/private/lib/billing/getOrgTierData.ts @@ -0,0 +1,46 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { getTierPriceSet } from "@server/lib/billing/tiers"; +import { getOrgSubscriptionData } from "#private/routers/billing/getOrgSubscription"; +import { build } from "@server/build"; + +export async function getOrgTierData( + orgId: string +): Promise<{ tier: string | null; active: boolean }> { + let tier = null; + let active = false; + + if (build !== "saas") { + return { tier, active }; + } + + const { subscription, items } = await getOrgSubscriptionData(orgId); + + if (items && items.length > 0) { + const tierPriceSet = getTierPriceSet(); + // Iterate through tiers in order (earlier keys are higher tiers) + for (const [tierId, priceId] of Object.entries(tierPriceSet)) { + // Check if any subscription item matches this tier's price ID + const matchingItem = items.find((item) => item.priceId === priceId); + if (matchingItem) { + tier = tierId; + break; + } + } + } + if (subscription && subscription.status === "active") { + active = true; + } + return { tier, active }; +} diff --git a/server/lib/private/billing/index.ts b/server/private/lib/billing/index.ts similarity index 81% rename from server/lib/private/billing/index.ts rename to server/private/lib/billing/index.ts index 0212ee1c..13ca3761 100644 --- a/server/lib/private/billing/index.ts +++ b/server/private/lib/billing/index.ts @@ -11,6 +11,5 @@ * This file is not licensed under the AGPLv3. */ -export * from "./limitSet"; -export * from "./features"; -export * from "./limitsService"; +export * from "./getOrgTierData"; +export * from "./createCustomer"; \ No newline at end of file diff --git a/server/lib/remoteCertificates/privateCertificates.ts b/server/private/lib/certificates.ts similarity index 97% rename from server/lib/remoteCertificates/privateCertificates.ts rename to server/private/lib/certificates.ts index fabc9ea5..0924daf7 100644 --- a/server/lib/remoteCertificates/privateCertificates.ts +++ b/server/private/lib/certificates.ts @@ -11,10 +11,10 @@ * This file is not licensed under the AGPLv3. */ -import config from "../config"; +import config from "./config"; import { certificates, db } from "@server/db"; import { and, eq, isNotNull } from "drizzle-orm"; -import { decryptData } from "../encryption"; +import { decryptData } from "@server/lib/encryption"; import * as fs from "fs"; export async function getValidCertificatesForDomains( diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts new file mode 100644 index 00000000..a0dbf9a3 --- /dev/null +++ b/server/private/lib/config.ts @@ -0,0 +1,163 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { z } from "zod"; +import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; +import { db } from "@server/db"; +import { SupporterKey, supporterKey } from "@server/db"; +import { eq } from "drizzle-orm"; +import { license } from "@server/license/license"; +import { fromError } from "zod-validation-error"; +import { + privateConfigSchema, + readPrivateConfigFile +} from "#private/lib/readConfigFile"; +import { build } from "@server/build"; + +export class PrivateConfig { + private rawPrivateConfig!: z.infer; + + supporterData: SupporterKey | null = null; + + supporterHiddenUntil: number | null = null; + + isDev: boolean = process.env.ENVIRONMENT !== "prod"; + + constructor() { + const privateEnvironment = readPrivateConfigFile(); + + const { + data: parsedPrivateConfig, + success: privateSuccess, + error: privateError + } = privateConfigSchema.safeParse(privateEnvironment); + + if (!privateSuccess) { + const errors = fromError(privateError); + throw new Error(`Invalid private configuration file: ${errors}`); + } + + if (parsedPrivateConfig.branding?.colors) { + process.env.BRANDING_COLORS = JSON.stringify( + parsedPrivateConfig.branding?.colors + ); + } + + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + if (build != "oss") { + if (parsedPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + parsedPrivateConfig.branding?.logo?.light_path; + } + if (parsedPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + parsedPrivateConfig.branding?.logo?.dark_path || undefined; + } + + process.env.BRANDING_LOGO_AUTH_WIDTH = parsedPrivateConfig.branding + ?.logo?.auth_page?.width + ? parsedPrivateConfig.branding?.logo?.auth_page?.width.toString() + : undefined; + process.env.BRANDING_LOGO_AUTH_HEIGHT = parsedPrivateConfig.branding + ?.logo?.auth_page?.height + ? parsedPrivateConfig.branding?.logo?.auth_page?.height.toString() + : undefined; + + process.env.BRANDING_LOGO_NAVBAR_WIDTH = parsedPrivateConfig + .branding?.logo?.navbar?.width + ? parsedPrivateConfig.branding?.logo?.navbar?.width.toString() + : undefined; + process.env.BRANDING_LOGO_NAVBAR_HEIGHT = parsedPrivateConfig + .branding?.logo?.navbar?.height + ? parsedPrivateConfig.branding?.logo?.navbar?.height.toString() + : undefined; + + process.env.BRANDING_FAVICON_PATH = + parsedPrivateConfig.branding?.favicon_path; + + process.env.BRANDING_APP_NAME = + parsedPrivateConfig.branding?.app_name || "Pangolin"; + + if (parsedPrivateConfig.branding?.footer) { + process.env.BRANDING_FOOTER = JSON.stringify( + parsedPrivateConfig.branding?.footer + ); + } + + process.env.LOGIN_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.title_text || ""; + process.env.LOGIN_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.login_page?.subtitle_text || ""; + + process.env.SIGNUP_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.title_text || ""; + process.env.SIGNUP_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.signup_page?.subtitle_text || ""; + + process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = + parsedPrivateConfig.branding?.resource_auth_page + ?.hide_powered_by === true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = + parsedPrivateConfig.branding?.resource_auth_page?.show_logo === + true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page?.title_text || + ""; + process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = + parsedPrivateConfig.branding?.resource_auth_page + ?.subtitle_text || ""; + + if (parsedPrivateConfig.branding?.background_image_path) { + process.env.BACKGROUND_IMAGE_PATH = + parsedPrivateConfig.branding?.background_image_path; + } + + if (parsedPrivateConfig.server.reo_client_id) { + process.env.REO_CLIENT_ID = + parsedPrivateConfig.server.reo_client_id; + } + + if (parsedPrivateConfig.stripe?.s3Bucket) { + process.env.S3_BUCKET = parsedPrivateConfig.stripe.s3Bucket; + } + if (parsedPrivateConfig.stripe?.localFilePath) { + process.env.LOCAL_FILE_PATH = parsedPrivateConfig.stripe.localFilePath; + } + if (parsedPrivateConfig.stripe?.s3Region) { + process.env.S3_REGION = parsedPrivateConfig.stripe.s3Region; + } + } + + this.rawPrivateConfig = parsedPrivateConfig; + } + + public getRawPrivateConfig() { + return this.rawPrivateConfig; + } +} + +export const privateConfig = new PrivateConfig(); + +export default privateConfig; diff --git a/server/lib/exitNodes/privateExitNodeComms.ts b/server/private/lib/exitNodes/exitNodeComms.ts similarity index 95% rename from server/lib/exitNodes/privateExitNodeComms.ts rename to server/private/lib/exitNodes/exitNodeComms.ts index 163a962f..4cf8fbe7 100644 --- a/server/lib/exitNodes/privateExitNodeComms.ts +++ b/server/private/lib/exitNodes/exitNodeComms.ts @@ -15,8 +15,9 @@ import axios from "axios"; import logger from "@server/logger"; import { db, ExitNode, remoteExitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../../routers/ws"; -import { config } from "../config"; +import { sendToClient } from "#private/routers/ws"; +import privateConfig from "#private/lib/config"; +import config from "@server/lib/config"; interface ExitNodeRequest { remoteType?: string; @@ -65,7 +66,7 @@ export async function sendToExitNode( logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); if (exitNode.name == config.getRawConfig().gerbil.exit_node_name) { - hostname = config.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; + hostname = privateConfig.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; } if (!hostname) { diff --git a/server/lib/exitNodes/privateExitNodes.ts b/server/private/lib/exitNodes/exitNodes.ts similarity index 100% rename from server/lib/exitNodes/privateExitNodes.ts rename to server/private/lib/exitNodes/exitNodes.ts diff --git a/server/private/lib/exitNodes/index.ts b/server/private/lib/exitNodes/index.ts new file mode 100644 index 00000000..098a0580 --- /dev/null +++ b/server/private/lib/exitNodes/index.ts @@ -0,0 +1,15 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./exitNodeComms"; +export * from "./exitNodes"; \ No newline at end of file diff --git a/server/db/private/rateLimit.test.ts b/server/private/lib/rateLimit.test.ts similarity index 100% rename from server/db/private/rateLimit.test.ts rename to server/private/lib/rateLimit.test.ts diff --git a/server/db/private/rateLimit.ts b/server/private/lib/rateLimit.ts similarity index 98% rename from server/db/private/rateLimit.ts rename to server/private/lib/rateLimit.ts index ff8589bc..1d0e62ae 100644 --- a/server/db/private/rateLimit.ts +++ b/server/private/lib/rateLimit.ts @@ -12,7 +12,7 @@ */ import logger from "@server/logger"; -import redisManager from "@server/db/private/redis"; +import redisManager from "@server/private/lib/redis"; import { build } from "@server/build"; // Rate limiting configuration @@ -451,8 +451,4 @@ export class RateLimitService { } // Export singleton instance -export const rateLimitService = new RateLimitService(); - -// Handle process termination -process.on("SIGTERM", () => rateLimitService.cleanup()); -process.on("SIGINT", () => rateLimitService.cleanup()); \ No newline at end of file +export const rateLimitService = new RateLimitService(); \ No newline at end of file diff --git a/server/private/lib/rateLimitStore.ts b/server/private/lib/rateLimitStore.ts new file mode 100644 index 00000000..11e1a9d6 --- /dev/null +++ b/server/private/lib/rateLimitStore.ts @@ -0,0 +1,32 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { build } from "@server/build"; +import privateConfig from "#private/lib/config"; +import { MemoryStore, Store } from "express-rate-limit"; +import RedisStore from "#private/lib/redisStore"; + +export function createStore(): Store { + if (build != "oss" && privateConfig.getRawPrivateConfig().flags?.enable_redis) { + const rateLimitStore: Store = new RedisStore({ + prefix: "api-rate-limit", // Optional: customize Redis key prefix + skipFailedRequests: true, // Don't count failed requests + skipSuccessfulRequests: false // Count successful requests + }); + + return rateLimitStore; + } else { + const rateLimitStore: Store = new MemoryStore(); + return rateLimitStore; + } +} diff --git a/server/lib/private/readConfigFile.ts b/server/private/lib/readConfigFile.ts similarity index 99% rename from server/lib/private/readConfigFile.ts rename to server/private/lib/readConfigFile.ts index ddc9ee82..c1847ba5 100644 --- a/server/lib/private/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -74,7 +74,6 @@ export const privateConfigSchema = z flags: z .object({ enable_redis: z.boolean().optional(), - hide_supporter_key: z.boolean().optional() }) .optional(), branding: z diff --git a/server/db/private/redis.ts b/server/private/lib/redis.ts similarity index 98% rename from server/db/private/redis.ts rename to server/private/lib/redis.ts index d6e67262..e74874f2 100644 --- a/server/db/private/redis.ts +++ b/server/private/lib/redis.ts @@ -13,7 +13,7 @@ import Redis, { RedisOptions } from "ioredis"; import logger from "@server/logger"; -import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import { build } from "@server/build"; class RedisManager { @@ -46,7 +46,7 @@ class RedisManager { this.isEnabled = false; return; } - this.isEnabled = config.getRawPrivateConfig().flags?.enable_redis || false; + this.isEnabled = privateConfig.getRawPrivateConfig().flags?.enable_redis || false; if (this.isEnabled) { this.initializeClients(); } @@ -93,7 +93,7 @@ class RedisManager { } private getRedisConfig(): RedisOptions { - const redisConfig = config.getRawPrivateConfig().redis!; + const redisConfig = privateConfig.getRawPrivateConfig().redis!; const opts: RedisOptions = { host: redisConfig.host!, port: redisConfig.port!, @@ -108,7 +108,7 @@ class RedisManager { } private getReplicaRedisConfig(): RedisOptions | null { - const redisConfig = config.getRawPrivateConfig().redis!; + const redisConfig = privateConfig.getRawPrivateConfig().redis!; if (!redisConfig.replicas || redisConfig.replicas.length === 0) { return null; } diff --git a/server/db/private/redisStore.ts b/server/private/lib/redisStore.ts similarity index 100% rename from server/db/private/redisStore.ts rename to server/private/lib/redisStore.ts diff --git a/server/lib/private/resend.ts b/server/private/lib/resend.ts similarity index 96% rename from server/lib/private/resend.ts rename to server/private/lib/resend.ts index 26c3f4a6..1aac3d07 100644 --- a/server/lib/private/resend.ts +++ b/server/private/lib/resend.ts @@ -12,7 +12,7 @@ */ import { Resend } from "resend"; -import config from "../config"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; export enum AudienceIds { @@ -22,7 +22,7 @@ export enum AudienceIds { } const resend = new Resend( - config.getRawPrivateConfig().server.resend_api_key || "missing" + privateConfig.getRawPrivateConfig().server.resend_api_key || "missing" ); export default resend; diff --git a/server/lib/private/stripe.ts b/server/private/lib/stripe.ts similarity index 84% rename from server/lib/private/stripe.ts rename to server/private/lib/stripe.ts index 1170202d..477934b4 100644 --- a/server/lib/private/stripe.ts +++ b/server/private/lib/stripe.ts @@ -12,13 +12,13 @@ */ import Stripe from "stripe"; -import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; import { build } from "@server/build"; let stripe: Stripe | undefined = undefined; if (build == "saas") { - const stripeApiKey = config.getRawPrivateConfig().stripe?.secret_key; + const stripeApiKey = privateConfig.getRawPrivateConfig().stripe?.secret_key; if (!stripeApiKey) { logger.error("Stripe secret key is not configured"); } diff --git a/server/lib/traefik/privateGetTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts similarity index 99% rename from server/lib/traefik/privateGetTraefikConfig.ts rename to server/private/lib/traefik/getTraefikConfig.ts index 1350e8b7..161ef48c 100644 --- a/server/lib/traefik/privateGetTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -25,7 +25,7 @@ import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; import { build } from "@server/build"; -import { sanitize } from "./utils"; +import { sanitize } from "@server/lib/traefik/utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; diff --git a/server/private/lib/traefik/index.ts b/server/private/lib/traefik/index.ts new file mode 100644 index 00000000..30d83181 --- /dev/null +++ b/server/private/lib/traefik/index.ts @@ -0,0 +1,14 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./getTraefikConfig"; \ No newline at end of file diff --git a/server/middlewares/private/index.ts b/server/private/middlewares/index.ts similarity index 92% rename from server/middlewares/private/index.ts rename to server/private/middlewares/index.ts index f034001d..c92b0d3d 100644 --- a/server/middlewares/private/index.ts +++ b/server/private/middlewares/index.ts @@ -15,4 +15,4 @@ export * from "./verifyCertificateAccess"; export * from "./verifyRemoteExitNodeAccess"; export * from "./verifyIdpAccess"; export * from "./verifyLoginPageAccess"; -export * from "./corsWithLoginPage"; \ No newline at end of file +export * from "../../lib/corsWithLoginPage"; \ No newline at end of file diff --git a/server/middlewares/private/verifyCertificateAccess.ts b/server/private/middlewares/verifyCertificateAccess.ts similarity index 100% rename from server/middlewares/private/verifyCertificateAccess.ts rename to server/private/middlewares/verifyCertificateAccess.ts diff --git a/server/middlewares/private/verifyIdpAccess.ts b/server/private/middlewares/verifyIdpAccess.ts similarity index 100% rename from server/middlewares/private/verifyIdpAccess.ts rename to server/private/middlewares/verifyIdpAccess.ts diff --git a/server/middlewares/private/verifyLoginPageAccess.ts b/server/private/middlewares/verifyLoginPageAccess.ts similarity index 100% rename from server/middlewares/private/verifyLoginPageAccess.ts rename to server/private/middlewares/verifyLoginPageAccess.ts diff --git a/server/middlewares/private/verifyRemoteExitNode.ts b/server/private/middlewares/verifyRemoteExitNode.ts similarity index 94% rename from server/middlewares/private/verifyRemoteExitNode.ts rename to server/private/middlewares/verifyRemoteExitNode.ts index 45c244e2..2f6d99d2 100644 --- a/server/middlewares/private/verifyRemoteExitNode.ts +++ b/server/private/middlewares/verifyRemoteExitNode.ts @@ -16,7 +16,7 @@ import ErrorResponse from "@server/types/ErrorResponse"; import config from "@server/lib/config"; import { unauthorized } from "@server/auth/unauthorizedResponse"; import logger from "@server/logger"; -import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; +import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remoteExitNode"; export const verifySessionRemoteExitNodeMiddleware = async ( req: any, diff --git a/server/middlewares/private/verifyRemoteExitNodeAccess.ts b/server/private/middlewares/verifyRemoteExitNodeAccess.ts similarity index 100% rename from server/middlewares/private/verifyRemoteExitNodeAccess.ts rename to server/private/middlewares/verifyRemoteExitNodeAccess.ts diff --git a/server/routers/auth/privateGetSessionTransferToken.ts b/server/private/routers/auth/getSessionTransferToken.ts similarity index 100% rename from server/routers/auth/privateGetSessionTransferToken.ts rename to server/private/routers/auth/getSessionTransferToken.ts diff --git a/server/private/routers/auth/index.ts b/server/private/routers/auth/index.ts new file mode 100644 index 00000000..39a60031 --- /dev/null +++ b/server/private/routers/auth/index.ts @@ -0,0 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./transferSession"; +export * from "./getSessionTransferToken"; +export * from "./quickStart"; \ No newline at end of file diff --git a/server/routers/auth/privateQuickStart.ts b/server/private/routers/auth/quickStart.ts similarity index 98% rename from server/routers/auth/privateQuickStart.ts rename to server/private/routers/auth/quickStart.ts index 683c24a8..582ac4d5 100644 --- a/server/routers/auth/privateQuickStart.ts +++ b/server/private/routers/auth/quickStart.ts @@ -50,16 +50,16 @@ import config from "@server/lib/config"; import logger from "@server/logger"; import { hashPassword } from "@server/auth/password"; import { UserType } from "@server/types/UserTypes"; -import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; +import { createUserAccountOrg } from "@server/lib/createUserAccountOrg"; import { sendEmail } from "@server/emails"; import WelcomeQuickStart from "@server/emails/templates/WelcomeQuickStart"; import { alphabet, generateRandomString } from "oslo/crypto"; import { createDate, TimeSpan } from "oslo"; import { getUniqueResourceName, getUniqueSiteName } from "@server/db/names"; -import { pickPort } from "../target/helpers"; -import { addTargets } from "../newt/targets"; +import { pickPort } from "@server/routers/target/helpers"; +import { addTargets } from "@server/routers/newt/targets"; import { isTargetValid } from "@server/lib/validators"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#private/lib/exitNodes"; const bodySchema = z.object({ email: z.string().toLowerCase().email(), diff --git a/server/routers/auth/privateTransferSession.ts b/server/private/routers/auth/transferSession.ts similarity index 100% rename from server/routers/auth/privateTransferSession.ts rename to server/private/routers/auth/transferSession.ts diff --git a/server/routers/private/billing/createCheckoutSession.ts b/server/private/routers/billing/createCheckoutSession.ts similarity index 95% rename from server/routers/private/billing/createCheckoutSession.ts rename to server/private/routers/billing/createCheckoutSession.ts index 67507b68..6e1e28c2 100644 --- a/server/routers/private/billing/createCheckoutSession.ts +++ b/server/private/routers/billing/createCheckoutSession.ts @@ -21,9 +21,9 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; -import stripe from "@server/lib/private/stripe"; -import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/private/billing"; -import { getTierPriceSet, TierId } from "@server/lib/private/billing/tiers"; +import stripe from "#private/lib/stripe"; +import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing"; +import { getTierPriceSet, TierId } from "@server/lib/billing/tiers"; const createCheckoutSessionSchema = z .object({ diff --git a/server/routers/private/billing/createPortalSession.ts b/server/private/routers/billing/createPortalSession.ts similarity index 98% rename from server/routers/private/billing/createPortalSession.ts rename to server/private/routers/billing/createPortalSession.ts index aa672377..eb55f007 100644 --- a/server/routers/private/billing/createPortalSession.ts +++ b/server/private/routers/billing/createPortalSession.ts @@ -21,7 +21,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; const createPortalSessionSchema = z .object({ diff --git a/server/routers/private/billing/getOrgSubscription.ts b/server/private/routers/billing/getOrgSubscription.ts similarity index 100% rename from server/routers/private/billing/getOrgSubscription.ts rename to server/private/routers/billing/getOrgSubscription.ts diff --git a/server/routers/private/billing/getOrgUsage.ts b/server/private/routers/billing/getOrgUsage.ts similarity index 96% rename from server/routers/private/billing/getOrgUsage.ts rename to server/private/routers/billing/getOrgUsage.ts index e1544e06..5aad9609 100644 --- a/server/routers/private/billing/getOrgUsage.ts +++ b/server/private/routers/billing/getOrgUsage.ts @@ -23,8 +23,8 @@ import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { Limit, limits, Usage, usage } from "@server/db"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const getOrgSchema = z .object({ diff --git a/server/routers/private/billing/hooks/handleCustomerCreated.ts b/server/private/routers/billing/hooks/handleCustomerCreated.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerCreated.ts rename to server/private/routers/billing/hooks/handleCustomerCreated.ts diff --git a/server/routers/private/billing/hooks/handleCustomerDeleted.ts b/server/private/routers/billing/hooks/handleCustomerDeleted.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerDeleted.ts rename to server/private/routers/billing/hooks/handleCustomerDeleted.ts diff --git a/server/routers/private/billing/hooks/handleCustomerUpdated.ts b/server/private/routers/billing/hooks/handleCustomerUpdated.ts similarity index 100% rename from server/routers/private/billing/hooks/handleCustomerUpdated.ts rename to server/private/routers/billing/hooks/handleCustomerUpdated.ts diff --git a/server/routers/private/billing/hooks/handleSubscriptionCreated.ts b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts similarity index 97% rename from server/routers/private/billing/hooks/handleSubscriptionCreated.ts rename to server/private/routers/billing/hooks/handleSubscriptionCreated.ts index ee6376c9..223a2545 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionCreated.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts @@ -22,9 +22,9 @@ import { } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; -import stripe from "@server/lib/private/stripe"; +import stripe from "#private/lib/stripe"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; +import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; export async function handleSubscriptionCreated( subscription: Stripe.Subscription diff --git a/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts similarity index 97% rename from server/routers/private/billing/hooks/handleSubscriptionDeleted.ts rename to server/private/routers/billing/hooks/handleSubscriptionDeleted.ts index 95123731..114a4b30 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionDeleted.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts @@ -16,7 +16,7 @@ import { subscriptions, db, subscriptionItems, customers, userOrgs, users } from import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "@server/lib/private/resend"; +import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; export async function handleSubscriptionDeleted( subscription: Stripe.Subscription diff --git a/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts b/server/private/routers/billing/hooks/handleSubscriptionUpdated.ts similarity index 98% rename from server/routers/private/billing/hooks/handleSubscriptionUpdated.ts rename to server/private/routers/billing/hooks/handleSubscriptionUpdated.ts index f1cbcafe..01086054 100644 --- a/server/routers/private/billing/hooks/handleSubscriptionUpdated.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionUpdated.ts @@ -23,8 +23,8 @@ import { } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; -import { getFeatureIdByMetricId } from "@server/lib/private/billing/features"; -import stripe from "@server/lib/private/stripe"; +import { getFeatureIdByMetricId } from "@server/lib/billing/features"; +import stripe from "#private/lib/stripe"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; export async function handleSubscriptionUpdated( diff --git a/server/routers/private/billing/index.ts b/server/private/routers/billing/index.ts similarity index 100% rename from server/routers/private/billing/index.ts rename to server/private/routers/billing/index.ts diff --git a/server/routers/private/billing/internalGetOrgTier.ts b/server/private/routers/billing/internalGetOrgTier.ts similarity index 68% rename from server/routers/private/billing/internalGetOrgTier.ts rename to server/private/routers/billing/internalGetOrgTier.ts index 7f8cc642..8db41807 100644 --- a/server/routers/private/billing/internalGetOrgTier.ts +++ b/server/private/routers/billing/internalGetOrgTier.ts @@ -18,9 +18,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; -import { getTierPriceSet } from "@server/lib/private/billing/tiers"; -import { getOrgSubscriptionData } from "./getOrgSubscription"; -import { build } from "@server/build"; +import { getOrgTierData } from "#private/lib/billing"; const getOrgSchema = z .object({ @@ -87,33 +85,3 @@ export async function getOrgTier( ); } } - -export async function getOrgTierData( - orgId: string -): Promise<{ tier: string | null; active: boolean }> { - let tier = null; - let active = false; - - if (build !== "saas") { - return { tier, active }; - } - - const { subscription, items } = await getOrgSubscriptionData(orgId); - - if (items && items.length > 0) { - const tierPriceSet = getTierPriceSet(); - // Iterate through tiers in order (earlier keys are higher tiers) - for (const [tierId, priceId] of Object.entries(tierPriceSet)) { - // Check if any subscription item matches this tier's price ID - const matchingItem = items.find((item) => item.priceId === priceId); - if (matchingItem) { - tier = tierId; - break; - } - } - } - if (subscription && subscription.status === "active") { - active = true; - } - return { tier, active }; -} diff --git a/server/routers/private/billing/subscriptionLifecycle.ts b/server/private/routers/billing/subscriptionLifecycle.ts similarity index 93% rename from server/routers/private/billing/subscriptionLifecycle.ts rename to server/private/routers/billing/subscriptionLifecycle.ts index 82dbfdbe..06b2a2a8 100644 --- a/server/routers/private/billing/subscriptionLifecycle.ts +++ b/server/private/routers/billing/subscriptionLifecycle.ts @@ -11,8 +11,8 @@ * This file is not licensed under the AGPLv3. */ -import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/private/billing"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { freeLimitSet, limitsService, subscribedLimitSet } from "@server/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; import logger from "@server/logger"; export async function handleSubscriptionLifesycle(orgId: string, status: string) { diff --git a/server/routers/private/billing/webhooks.ts b/server/private/routers/billing/webhooks.ts similarity index 95% rename from server/routers/private/billing/webhooks.ts rename to server/private/routers/billing/webhooks.ts index 2844943a..24ad1074 100644 --- a/server/routers/private/billing/webhooks.ts +++ b/server/private/routers/billing/webhooks.ts @@ -11,8 +11,8 @@ * This file is not licensed under the AGPLv3. */ -import stripe from "@server/lib/private/stripe"; -import config from "@server/lib/config"; +import stripe from "#private/lib/stripe"; +import privateConfig from "#private/lib/config"; import logger from "@server/logger"; import createHttpError from "http-errors"; import { response } from "@server/lib/response"; @@ -26,13 +26,13 @@ import { handleCustomerUpdated } from "./hooks/handleCustomerUpdated"; import { handleSubscriptionDeleted } from "./hooks/handleSubscriptionDeleted"; import { handleCustomerDeleted } from "./hooks/handleCustomerDeleted"; -export async function stripeWebhookHandler( +export async function billingWebhookHandler( req: Request, res: Response, next: NextFunction ): Promise { let event: Stripe.Event = req.body; - const endpointSecret = config.getRawPrivateConfig().stripe?.webhook_secret; + const endpointSecret = privateConfig.getRawPrivateConfig().stripe?.webhook_secret; if (!endpointSecret) { logger.warn("Stripe webhook secret is not configured. Webhook events will not be priocessed."); return next( diff --git a/server/routers/private/certificates/createCertificate.ts b/server/private/routers/certificates/createCertificate.ts similarity index 100% rename from server/routers/private/certificates/createCertificate.ts rename to server/private/routers/certificates/createCertificate.ts diff --git a/server/routers/private/certificates/getCertificate.ts b/server/private/routers/certificates/getCertificate.ts similarity index 100% rename from server/routers/private/certificates/getCertificate.ts rename to server/private/routers/certificates/getCertificate.ts diff --git a/server/routers/private/certificates/index.ts b/server/private/routers/certificates/index.ts similarity index 100% rename from server/routers/private/certificates/index.ts rename to server/private/routers/certificates/index.ts diff --git a/server/routers/private/certificates/restartCertificate.ts b/server/private/routers/certificates/restartCertificate.ts similarity index 100% rename from server/routers/private/certificates/restartCertificate.ts rename to server/private/routers/certificates/restartCertificate.ts diff --git a/server/routers/domain/privateCheckDomainNamespaceAvailability.ts b/server/private/routers/domain/checkDomainNamespaceAvailability.ts similarity index 100% rename from server/routers/domain/privateCheckDomainNamespaceAvailability.ts rename to server/private/routers/domain/checkDomainNamespaceAvailability.ts diff --git a/server/private/routers/domain/index.ts b/server/private/routers/domain/index.ts new file mode 100644 index 00000000..da9cec3f --- /dev/null +++ b/server/private/routers/domain/index.ts @@ -0,0 +1,15 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./checkDomainNamespaceAvailability"; +export * from "./listDomainNamespaces"; \ No newline at end of file diff --git a/server/routers/domain/privateListDomainNamespaces.ts b/server/private/routers/domain/listDomainNamespaces.ts similarity index 100% rename from server/routers/domain/privateListDomainNamespaces.ts rename to server/private/routers/domain/listDomainNamespaces.ts diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts new file mode 100644 index 00000000..fac7c0c4 --- /dev/null +++ b/server/private/routers/external.ts @@ -0,0 +1,262 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as certificates from "#private/routers/certificates"; +import { createStore } from "#private/lib/rateLimitStore"; +import * as billing from "#private/routers/billing"; +import * as remoteExitNode from "#private/routers/remoteExitNode"; +import * as loginPage from "#private/routers/loginPage"; +import * as orgIdp from "#private/routers/orgIdp"; +import * as domain from "#private/routers/domain"; +import * as auth from "#private/routers/auth"; + +import { Router } from "express"; +import { verifyOrgAccess, verifySessionUserMiddleware, verifyUserHasAction } from "@server/middlewares"; +import { ActionsEnum } from "@server/auth/actions"; +import { + verifyCertificateAccess, + verifyIdpAccess, + verifyLoginPageAccess, + verifyRemoteExitNodeAccess +} from "#private/middlewares"; +import rateLimit, { ipKeyGenerator } from "express-rate-limit"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +import { unauthenticated as ua, authenticated as a } from "@server/routers/external"; + +export const authenticated = a; +export const unauthenticated = ua; + +unauthenticated.post( + "/quick-start", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + keyGenerator: (req) => req.path, + handler: (req, res, next) => { + const message = `We're too busy right now. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + auth.quickStart +); + +unauthenticated.post( + "/remote-exit-node/quick-start", + rateLimit({ + windowMs: 60 * 60 * 1000, + max: 5, + keyGenerator: (req) => `${req.path}:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only create 5 remote exit nodes every hour. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + remoteExitNode.quickStartRemoteExitNode +); + + +authenticated.put( + "/org/:orgId/idp/oidc", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createIdp), + orgIdp.createOrgOidcIdp +); + +authenticated.post( + "/org/:orgId/idp/:idpId/oidc", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.updateIdp), + orgIdp.updateOrgOidcIdp +); + +authenticated.delete( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.deleteIdp), + orgIdp.deleteOrgIdp +); + +authenticated.get( + "/org/:orgId/idp/:idpId", + verifyOrgAccess, + verifyIdpAccess, + verifyUserHasAction(ActionsEnum.getIdp), + orgIdp.getOrgIdp +); + +authenticated.get( + "/org/:orgId/idp", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listIdps), + orgIdp.listOrgIdps +); + +authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids + +authenticated.get( + "/org/:orgId/certificate/:domainId/:domain", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.getCertificate), + certificates.getCertificate +); + +authenticated.post( + "/org/:orgId/certificate/:certId/restart", + verifyOrgAccess, + verifyCertificateAccess, + verifyUserHasAction(ActionsEnum.restartCertificate), + certificates.restartCertificate +); + +authenticated.post( + "/org/:orgId/billing/create-checkout-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createCheckoutSession +); + +authenticated.post( + "/org/:orgId/billing/create-portal-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createPortalSession +); + +authenticated.get( + "/org/:orgId/billing/subscription", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgSubscription +); + +authenticated.get( + "/org/:orgId/billing/usage", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgUsage +); + +authenticated.get("/domain/namespaces", domain.listDomainNamespaces); + +authenticated.get( + "/domain/check-namespace-availability", + domain.checkDomainNamespaceAvailability +); + +authenticated.put( + "/org/:orgId/remote-exit-node", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.createRemoteExitNode +); + +authenticated.get( + "/org/:orgId/remote-exit-nodes", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.listRemoteExitNode), + remoteExitNode.listRemoteExitNodes +); + +authenticated.get( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.getRemoteExitNode), + remoteExitNode.getRemoteExitNode +); + +authenticated.get( + "/org/:orgId/pick-remote-exit-node-defaults", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createRemoteExitNode), + remoteExitNode.pickRemoteExitNodeDefaults +); + +authenticated.delete( + "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyOrgAccess, + verifyRemoteExitNodeAccess, + verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), + remoteExitNode.deleteRemoteExitNode +); + +authenticated.put( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.createLoginPage), + loginPage.createLoginPage +); + +authenticated.post( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.updateLoginPage), + loginPage.updateLoginPage +); + +authenticated.delete( + "/org/:orgId/login-page/:loginPageId", + verifyOrgAccess, + verifyLoginPageAccess, + verifyUserHasAction(ActionsEnum.deleteLoginPage), + loginPage.deleteLoginPage +); + +authenticated.get( + "/org/:orgId/login-page", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.getLoginPage), + loginPage.getLoginPage +); + +export const authRouter = Router(); + +authRouter.post( + "/remoteExitNode/get-token", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 900, + keyGenerator: (req) => + `remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + remoteExitNode.getRemoteExitNodeToken +); + +authRouter.post( + "/transfer-session-token", + rateLimit({ + windowMs: 1 * 60 * 1000, + max: 60, + keyGenerator: (req) => + `transferSessionToken:${ipKeyGenerator(req.ip || "")}`, + handler: (req, res, next) => { + const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + }, + store: createStore() + }), + auth.transferSession +); \ No newline at end of file diff --git a/server/routers/gerbil/privateCreateExitNode.ts b/server/private/routers/gerbil/createExitNode.ts similarity index 100% rename from server/routers/gerbil/privateCreateExitNode.ts rename to server/private/routers/gerbil/createExitNode.ts diff --git a/server/routers/gerbil/privateReceiveBandwidth.ts b/server/private/routers/gerbil/receiveBandwidth.ts similarity index 100% rename from server/routers/gerbil/privateReceiveBandwidth.ts rename to server/private/routers/gerbil/receiveBandwidth.ts diff --git a/server/routers/private/hybrid.ts b/server/private/routers/hybrid.ts similarity index 98% rename from server/routers/private/hybrid.ts rename to server/private/routers/hybrid.ts index 2b59aa40..6d817853 100644 --- a/server/routers/private/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -11,7 +11,7 @@ * This file is not licensed under the AGPLv3. */ -import { verifySessionRemoteExitNodeMiddleware } from "@server/middlewares/private/verifyRemoteExitNode"; +import { verifySessionRemoteExitNodeMiddleware } from "#private/middlewares/verifyRemoteExitNode"; import { Router } from "express"; import { db, @@ -55,21 +55,22 @@ import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; -import { getTraefikConfig } from "../../lib/traefik"; +import { getTraefikConfig } from "#private/lib/traefik"; import { generateGerbilConfig, generateRelayMappings, updateAndGenerateEndpointDestinations, updateSiteBandwidth -} from "../gerbil"; +} from "@server/routers/gerbil"; import * as gerbil from "@server/routers/gerbil"; import logger from "@server/logger"; import { decryptData } from "@server/lib/encryption"; -import { config } from "@server/lib/config"; +import config from "@server/lib/config"; +import privateConfig from "#private/lib/config"; import * as fs from "fs"; -import { exchangeSession } from "../badger"; +import { exchangeSession } from "@server/routers/badger"; import { validateResourceSessionToken } from "@server/auth/sessions/resource"; -import { checkExitNodeOrg, resolveExitNodes } from "@server/lib/exitNodes"; +import { checkExitNodeOrg, resolveExitNodes } from "#private/lib/exitNodes"; import { maxmindLookup } from "@server/db/maxmind"; // Zod schemas for request validation @@ -211,7 +212,7 @@ export type UserSessionWithUser = { }; // Root routes -const hybridRouter = Router(); +export const hybridRouter = Router(); hybridRouter.use(verifySessionRemoteExitNodeMiddleware); hybridRouter.get( @@ -387,7 +388,7 @@ hybridRouter.get( } const encryptionKeyPath = - config.getRawPrivateConfig().server.encryption_key_path; + privateConfig.getRawPrivateConfig().server.encryption_key_path; if (!fs.existsSync(encryptionKeyPath)) { throw new Error( @@ -1488,6 +1489,4 @@ hybridRouter.post( ); } } -); - -export default hybridRouter; +); \ No newline at end of file diff --git a/server/private/routers/integration.ts b/server/private/routers/integration.ts new file mode 100644 index 00000000..d767424a --- /dev/null +++ b/server/private/routers/integration.ts @@ -0,0 +1,42 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as orgIdp from "#private/routers/orgIdp"; +import * as org from "#private/routers/org"; + +import { Router } from "express"; +import { + verifyApiKey, + verifyApiKeyHasAction, + verifyApiKeyIsRoot, +} from "@server/middlewares"; +import { ActionsEnum } from "@server/auth/actions"; + +import { unauthenticated as ua, authenticated as a } from "@server/routers/integration"; + +export const unauthenticated = ua; +export const authenticated = a; + +authenticated.post( + `/org/:orgId/send-usage-notification`, + verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine + verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), + org.sendUsageNotification +); + +authenticated.delete( + "/idp/:idpId", + verifyApiKeyIsRoot, + verifyApiKeyHasAction(ActionsEnum.deleteIdp), + orgIdp.deleteOrgIdp +); \ No newline at end of file diff --git a/server/private/routers/internal.ts b/server/private/routers/internal.ts new file mode 100644 index 00000000..ab3db1ce --- /dev/null +++ b/server/private/routers/internal.ts @@ -0,0 +1,36 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import * as loginPage from "#private/routers/loginPage"; +import * as auth from "#private/routers/auth"; +import * as orgIdp from "#private/routers/orgIdp"; +import * as billing from "#private/routers/billing"; + +import { Router } from "express"; +import { verifySessionUserMiddleware } from "@server/middlewares"; + +import { internalRouter as ir } from "@server/routers/internal"; + +export const internalRouter = ir; + +internalRouter.get("/org/:orgId/idp", orgIdp.listOrgIdps); + +internalRouter.get("/org/:orgId/billing/tier", billing.getOrgTier); + +internalRouter.get("/login-page", loginPage.loadLoginPage); + +internalRouter.post( + "/get-session-transfer-token", + verifySessionUserMiddleware, + auth.getSessionTransferToken +); diff --git a/server/routers/private/loginPage/createLoginPage.ts b/server/private/routers/loginPage/createLoginPage.ts similarity index 96% rename from server/routers/private/loginPage/createLoginPage.ts rename to server/private/routers/loginPage/createLoginPage.ts index fca29aae..9c0359fe 100644 --- a/server/routers/private/loginPage/createLoginPage.ts +++ b/server/private/routers/loginPage/createLoginPage.ts @@ -29,9 +29,9 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq, and } from "drizzle-orm"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { createCertificate } from "#private/routers/certificates/createCertificate"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; const paramsSchema = z diff --git a/server/routers/private/loginPage/deleteLoginPage.ts b/server/private/routers/loginPage/deleteLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/deleteLoginPage.ts rename to server/private/routers/loginPage/deleteLoginPage.ts diff --git a/server/routers/private/loginPage/getLoginPage.ts b/server/private/routers/loginPage/getLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/getLoginPage.ts rename to server/private/routers/loginPage/getLoginPage.ts diff --git a/server/routers/private/loginPage/index.ts b/server/private/routers/loginPage/index.ts similarity index 100% rename from server/routers/private/loginPage/index.ts rename to server/private/routers/loginPage/index.ts diff --git a/server/routers/private/loginPage/loadLoginPage.ts b/server/private/routers/loginPage/loadLoginPage.ts similarity index 100% rename from server/routers/private/loginPage/loadLoginPage.ts rename to server/private/routers/loginPage/loadLoginPage.ts diff --git a/server/routers/private/loginPage/updateLoginPage.ts b/server/private/routers/loginPage/updateLoginPage.ts similarity index 96% rename from server/routers/private/loginPage/updateLoginPage.ts rename to server/private/routers/loginPage/updateLoginPage.ts index 9c19913d..4aebbb7a 100644 --- a/server/routers/private/loginPage/updateLoginPage.ts +++ b/server/private/routers/loginPage/updateLoginPage.ts @@ -22,9 +22,9 @@ import { fromError } from "zod-validation-error"; import { eq, and } from "drizzle-orm"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { subdomainSchema } from "@server/lib/schemas"; -import { createCertificate } from "@server/routers/private/certificates/createCertificate"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { createCertificate } from "#private/routers/certificates/createCertificate"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; const paramsSchema = z diff --git a/server/private/routers/org/index.ts b/server/private/routers/org/index.ts new file mode 100644 index 00000000..189c5323 --- /dev/null +++ b/server/private/routers/org/index.ts @@ -0,0 +1,14 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +export * from "./sendUsageNotifications"; \ No newline at end of file diff --git a/server/routers/org/privateSendUsageNotifications.ts b/server/private/routers/org/sendUsageNotifications.ts similarity index 98% rename from server/routers/org/privateSendUsageNotifications.ts rename to server/private/routers/org/sendUsageNotifications.ts index 8b2a773d..3ef27f91 100644 --- a/server/routers/org/privateSendUsageNotifications.ts +++ b/server/private/routers/org/sendUsageNotifications.ts @@ -22,8 +22,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { sendEmail } from "@server/emails"; -import NotifyUsageLimitApproaching from "@server/emails/templates/PrivateNotifyUsageLimitApproaching"; -import NotifyUsageLimitReached from "@server/emails/templates/PrivateNotifyUsageLimitReached"; +import NotifyUsageLimitApproaching from "@server/emails/templates/NotifyUsageLimitApproaching"; +import NotifyUsageLimitReached from "@server/emails/templates/NotifyUsageLimitReached"; import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; diff --git a/server/routers/private/orgIdp/createOrgOidcIdp.ts b/server/private/routers/orgIdp/createOrgOidcIdp.ts similarity index 97% rename from server/routers/private/orgIdp/createOrgOidcIdp.ts rename to server/private/routers/orgIdp/createOrgOidcIdp.ts index 16697f98..5f732864 100644 --- a/server/routers/private/orgIdp/createOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/createOrgOidcIdp.ts @@ -25,8 +25,8 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict(); diff --git a/server/private/routers/orgIdp/deleteOrgIdp.ts b/server/private/routers/orgIdp/deleteOrgIdp.ts new file mode 100644 index 00000000..711d1ce3 --- /dev/null +++ b/server/private/routers/orgIdp/deleteOrgIdp.ts @@ -0,0 +1,108 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { 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 { idp, idpOidcConfig, idpOrg } from "@server/db"; +import { eq } from "drizzle-orm"; +import { OpenAPITags, registry } from "@server/openApi"; + +const paramsSchema = z + .object({ + orgId: z.string().optional(), // Optional; used with org idp in saas + idpId: z.coerce.number() + }) + .strict(); + +registry.registerPath({ + method: "delete", + path: "/idp/{idpId}", + description: "Delete IDP.", + tags: [OpenAPITags.Idp], + request: { + params: paramsSchema + }, + responses: {} +}); + +export async function deleteOrgIdp( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = paramsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const { idpId } = parsedParams.data; + + // Check if IDP exists + const [existingIdp] = await db + .select() + .from(idp) + .where(eq(idp.idpId, idpId)); + + if (!existingIdp) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "IdP not found" + ) + ); + } + + // Delete the IDP and its related records in a transaction + await db.transaction(async (trx) => { + // Delete OIDC config if it exists + await trx + .delete(idpOidcConfig) + .where(eq(idpOidcConfig.idpId, idpId)); + + // Delete IDP-org mappings + await trx + .delete(idpOrg) + .where(eq(idpOrg.idpId, idpId)); + + // Delete the IDP itself + await trx + .delete(idp) + .where(eq(idp.idpId, idpId)); + }); + + return response(res, { + data: null, + success: true, + error: false, + message: "IdP deleted successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/private/orgIdp/getOrgIdp.ts b/server/private/routers/orgIdp/getOrgIdp.ts similarity index 100% rename from server/routers/private/orgIdp/getOrgIdp.ts rename to server/private/routers/orgIdp/getOrgIdp.ts diff --git a/server/routers/private/orgIdp/index.ts b/server/private/routers/orgIdp/index.ts similarity index 87% rename from server/routers/private/orgIdp/index.ts rename to server/private/routers/orgIdp/index.ts index 99c30654..562582c6 100644 --- a/server/routers/private/orgIdp/index.ts +++ b/server/private/routers/orgIdp/index.ts @@ -14,4 +14,5 @@ export * from "./createOrgOidcIdp"; export * from "./getOrgIdp"; export * from "./listOrgIdps"; -export * from "./updateOrgOidcIdp"; \ No newline at end of file +export * from "./updateOrgOidcIdp"; +export * from "./deleteOrgIdp"; \ No newline at end of file diff --git a/server/routers/private/orgIdp/listOrgIdps.ts b/server/private/routers/orgIdp/listOrgIdps.ts similarity index 100% rename from server/routers/private/orgIdp/listOrgIdps.ts rename to server/private/routers/orgIdp/listOrgIdps.ts diff --git a/server/routers/private/orgIdp/updateOrgOidcIdp.ts b/server/private/routers/orgIdp/updateOrgOidcIdp.ts similarity index 97% rename from server/routers/private/orgIdp/updateOrgOidcIdp.ts rename to server/private/routers/orgIdp/updateOrgOidcIdp.ts index a3be85c3..c6e54240 100644 --- a/server/routers/private/orgIdp/updateOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/updateOrgOidcIdp.ts @@ -24,10 +24,9 @@ import { idp, idpOidcConfig } from "@server/db"; import { eq, and } from "drizzle-orm"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; -import license from "@server/license/license"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/private/remoteExitNode/createRemoteExitNode.ts b/server/private/routers/remoteExitNode/createRemoteExitNode.ts similarity index 97% rename from server/routers/private/remoteExitNode/createRemoteExitNode.ts rename to server/private/routers/remoteExitNode/createRemoteExitNode.ts index ac4fd231..44fecb86 100644 --- a/server/routers/private/remoteExitNode/createRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/createRemoteExitNode.ts @@ -21,14 +21,14 @@ import response from "@server/lib/response"; import { SqliteError } from "better-sqlite3"; import moment from "moment"; import { generateSessionToken } from "@server/auth/sessions/app"; -import { createRemoteExitNodeSession } from "@server/auth/sessions/privateRemoteExitNode"; +import { createRemoteExitNodeSession } from "#private/auth/sessions/remoteExitNode"; import { fromError } from "zod-validation-error"; import { hashPassword, verifyPassword } from "@server/auth/password"; import logger from "@server/logger"; import { and, eq } from "drizzle-orm"; import { getNextAvailableSubnet } from "@server/lib/exitNodes"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; export const paramsSchema = z.object({ orgId: z.string() diff --git a/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts similarity index 96% rename from server/routers/private/remoteExitNode/deleteRemoteExitNode.ts rename to server/private/routers/remoteExitNode/deleteRemoteExitNode.ts index 84ef0fab..f7b9d56c 100644 --- a/server/routers/private/remoteExitNode/deleteRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/deleteRemoteExitNode.ts @@ -21,8 +21,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const paramsSchema = z .object({ diff --git a/server/routers/private/remoteExitNode/getRemoteExitNode.ts b/server/private/routers/remoteExitNode/getRemoteExitNode.ts similarity index 100% rename from server/routers/private/remoteExitNode/getRemoteExitNode.ts rename to server/private/routers/remoteExitNode/getRemoteExitNode.ts diff --git a/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts similarity index 98% rename from server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts rename to server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts index 3905f1f7..48b0110d 100644 --- a/server/routers/private/remoteExitNode/getRemoteExitNodeToken.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts @@ -24,7 +24,7 @@ import { fromError } from "zod-validation-error"; import { createRemoteExitNodeSession, validateRemoteExitNodeSessionToken -} from "@server/auth/sessions/privateRemoteExitNode"; +} from "#private/auth/sessions/remoteExitNode"; import { verifyPassword } from "@server/auth/password"; import logger from "@server/logger"; import config from "@server/lib/config"; diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts similarity index 100% rename from server/routers/private/remoteExitNode/handleRemoteExitNodePingMessage.ts rename to server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts diff --git a/server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts b/server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts similarity index 100% rename from server/routers/private/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts rename to server/private/routers/remoteExitNode/handleRemoteExitNodeRegisterMessage.ts diff --git a/server/routers/private/remoteExitNode/index.ts b/server/private/routers/remoteExitNode/index.ts similarity index 100% rename from server/routers/private/remoteExitNode/index.ts rename to server/private/routers/remoteExitNode/index.ts diff --git a/server/routers/private/remoteExitNode/listRemoteExitNodes.ts b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts similarity index 100% rename from server/routers/private/remoteExitNode/listRemoteExitNodes.ts rename to server/private/routers/remoteExitNode/listRemoteExitNodes.ts diff --git a/server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts similarity index 100% rename from server/routers/private/remoteExitNode/pickRemoteExitNodeDefaults.ts rename to server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts diff --git a/server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts similarity index 100% rename from server/routers/private/remoteExitNode/quickStartRemoteExitNode.ts rename to server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts diff --git a/src/lib/types/privateThemeTypes.tsx b/server/private/routers/ws/index.ts similarity index 94% rename from src/lib/types/privateThemeTypes.tsx rename to server/private/routers/ws/index.ts index de0b2d2b..4d803a3a 100644 --- a/src/lib/types/privateThemeTypes.tsx +++ b/server/private/routers/ws/index.ts @@ -11,3 +11,4 @@ * This file is not licensed under the AGPLv3. */ +export * from "./ws"; \ No newline at end of file diff --git a/server/private/routers/ws/messageHandlers.ts b/server/private/routers/ws/messageHandlers.ts new file mode 100644 index 00000000..71c2b253 --- /dev/null +++ b/server/private/routers/ws/messageHandlers.ts @@ -0,0 +1,26 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { + handleRemoteExitNodeRegisterMessage, + handleRemoteExitNodePingMessage, + startRemoteExitNodeOfflineChecker +} from "#private/routers/remoteExitNode"; +import { MessageHandler } from "@server/routers/ws"; + +export const messageHandlers: Record = { + "remoteExitNode/register": handleRemoteExitNodeRegisterMessage, + "remoteExitNode/ping": handleRemoteExitNodePingMessage +}; + +startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes \ No newline at end of file diff --git a/server/routers/ws/privateWs.ts b/server/private/routers/ws/ws.ts similarity index 94% rename from server/routers/ws/privateWs.ts rename to server/private/routers/ws/ws.ts index d94ccf5e..0122126f 100644 --- a/server/routers/ws/privateWs.ts +++ b/server/private/routers/ws/ws.ts @@ -14,7 +14,6 @@ import { Router, Request, Response } from "express"; import { Server as HttpServer } from "http"; import { WebSocket, WebSocketServer } from "ws"; -import { IncomingMessage } from "http"; import { Socket } from "net"; import { Newt, @@ -31,73 +30,20 @@ import { eq } from "drizzle-orm"; import { db } from "@server/db"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; import { validateOlmSessionToken } from "@server/auth/sessions/olm"; -import { messageHandlers } from "./messageHandlers"; import logger from "@server/logger"; -import redisManager from "@server/db/private/redis"; +import redisManager from "#private/lib/redis"; import { v4 as uuidv4 } from "uuid"; -import { validateRemoteExitNodeSessionToken } from "@server/auth/sessions/privateRemoteExitNode"; -import { rateLimitService } from "@server/db/private/rateLimit"; +import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remoteExitNode"; +import { rateLimitService } from "#private/lib/rateLimit"; +import { messageHandlers } from "@server/routers/ws/messageHandlers"; +import { messageHandlers as privateMessageHandlers } from "#private/routers/ws/messageHandlers"; +import { AuthenticatedWebSocket, ClientType, WSMessage, TokenPayload, WebSocketRequest, RedisMessage } from "@server/routers/ws"; + +// Merge public and private message handlers +Object.assign(messageHandlers, privateMessageHandlers); const MAX_PENDING_MESSAGES = 50; // Maximum messages to queue during connection setup -// Custom interfaces -interface WebSocketRequest extends IncomingMessage { - token?: string; -} - -type ClientType = "newt" | "olm" | "remoteExitNode"; - -interface AuthenticatedWebSocket extends WebSocket { - client?: Newt | Olm | RemoteExitNode; - clientType?: ClientType; - connectionId?: string; - isFullyConnected?: boolean; - pendingMessages?: Buffer[]; -} - -interface TokenPayload { - client: Newt | Olm | RemoteExitNode; - session: NewtSession | OlmSession | RemoteExitNodeSession; - clientType: ClientType; -} - -interface WSMessage { - type: string; - data: any; -} - -interface HandlerResponse { - message: WSMessage; - broadcast?: boolean; - excludeSender?: boolean; - targetClientId?: string; -} - -interface HandlerContext { - message: WSMessage; - senderWs: WebSocket; - client: Newt | Olm | RemoteExitNode | undefined; - clientType: ClientType; - sendToClient: (clientId: string, message: WSMessage) => Promise; - broadcastToAllExcept: ( - message: WSMessage, - excludeClientId?: string - ) => Promise; - connectedClients: Map; -} - -interface RedisMessage { - type: "direct" | "broadcast"; - targetClientId?: string; - excludeClientId?: string; - message: WSMessage; - fromNodeId: string; -} - -export type MessageHandler = ( - context: HandlerContext -) => Promise; - // Helper function to process a single message const processMessage = async ( ws: AuthenticatedWebSocket, @@ -875,10 +821,6 @@ const cleanup = async (): Promise => { } }; -// Handle process termination -process.on("SIGTERM", cleanup); -process.on("SIGINT", cleanup); - export { router, handleWSUpgrade, diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 9db5931a..754478fc 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -10,10 +10,7 @@ export * from "./resetPassword"; export * from "./requestPasswordReset"; export * from "./setServerAdmin"; export * from "./initialSetupComplete"; -export * from "./privateQuickStart"; export * from "./validateSetupToken"; export * from "./changePassword"; export * from "./checkResourceSession"; -export * from "./securityKey"; -export * from "./privateGetSessionTransferToken"; -export * from "./privateTransferSession"; +export * from "./securityKey"; \ No newline at end of file diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index e6ae4fe4..7c122a44 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -110,10 +110,12 @@ export async function requestTotpSecret( ); } + const appName = process.env.BRANDING_APP_NAME || "Pangolin"; // From the private config loading into env vars to seperate away the private config + const hex = crypto.getRandomValues(new Uint8Array(20)); const secret = encodeHex(hex); const uri = createTOTPKeyURI( - config.getRawPrivateConfig().branding?.app_name || "Pangolin", + appName, user.email!, hex ); diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index 0d4f6865..1f361b79 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -21,12 +21,12 @@ import { hashPassword } from "@server/auth/password"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { passwordSchema } from "@server/auth/passwordSchema"; import { UserType } from "@server/types/UserTypes"; -import { createUserAccountOrg } from "@server/lib/private/createUserAccountOrg"; +import { createUserAccountOrg } from "@server/lib/createUserAccountOrg"; import { build } from "@server/build"; import resend, { AudienceIds, moveEmailToAudience -} from "@server/lib/private/resend"; +} from "#dynamic/lib/resend"; export const signupBodySchema = z.object({ email: z.string().toLowerCase().email(), diff --git a/server/routers/auth/verifyEmail.ts b/server/routers/auth/verifyEmail.ts index 010ddf28..47a81c0a 100644 --- a/server/routers/auth/verifyEmail.ts +++ b/server/routers/auth/verifyEmail.ts @@ -10,7 +10,7 @@ import { eq } from "drizzle-orm"; import { isWithinExpirationDate } from "oslo"; import config from "@server/lib/config"; import logger from "@server/logger"; -import { freeLimitSet, limitsService } from "@server/lib/private/billing"; +import { freeLimitSet, limitsService } from "@server/lib/billing"; import { build } from "@server/build"; export const verifyEmailBody = z diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index c380e679..4a000144 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -34,8 +34,8 @@ import NodeCache from "node-cache"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import { getCountryCodeForIp, remoteGetCountryCodeForIp } from "@server/lib/geoip"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { verifyPassword } from "@server/auth/password"; // We'll see if this speeds anything up diff --git a/server/routers/billing/webhooks.ts b/server/routers/billing/webhooks.ts new file mode 100644 index 00000000..0ca38a8a --- /dev/null +++ b/server/routers/billing/webhooks.ts @@ -0,0 +1,14 @@ +import createHttpError from "http-errors"; +import { Request, Response, NextFunction } from "express"; +import HttpCode from "@server/types/HttpCode"; + +export async function billingWebhookHandler( + req: Request, + res: Response, + next: NextFunction +): Promise { + // return not found + return next( + createHttpError(HttpCode.NOT_FOUND, "This endpoint is not in use") + ); +} \ No newline at end of file diff --git a/server/routers/certificates/createCertificate.ts b/server/routers/certificates/createCertificate.ts new file mode 100644 index 00000000..e160e644 --- /dev/null +++ b/server/routers/certificates/createCertificate.ts @@ -0,0 +1,5 @@ +import { db, Transaction } from "@server/db"; + +export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { + return; +} \ No newline at end of file diff --git a/server/routers/client/createClient.ts b/server/routers/client/createClient.ts index e7762223..cb2bbd6e 100644 --- a/server/routers/client/createClient.ts +++ b/server/routers/client/createClient.ts @@ -24,7 +24,7 @@ import { hashPassword } from "@server/auth/password"; import { isValidCIDR, isValidIP } from "@server/lib/validators"; import { isIpInCidr } from "@server/lib/ip"; import { OpenAPITags, registry } from "@server/openApi"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; const createClientParamsSchema = z .object({ diff --git a/server/routers/client/targets.ts b/server/routers/client/targets.ts index e34a23e9..e5c46d70 100644 --- a/server/routers/client/targets.ts +++ b/server/routers/client/targets.ts @@ -1,4 +1,4 @@ -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; export async function addTargets( newtId: string, diff --git a/server/routers/client/updateClient.ts b/server/routers/client/updateClient.ts index 80050f6c..884a9864 100644 --- a/server/routers/client/updateClient.ts +++ b/server/routers/client/updateClient.ts @@ -17,7 +17,7 @@ import { addPeer as olmAddPeer, deletePeer as olmDeletePeer } from "../olm/peers"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; const updateClientParamsSchema = z .object({ diff --git a/server/routers/domain/createOrgDomain.ts b/server/routers/domain/createOrgDomain.ts index 3744f044..4e2acdbf 100644 --- a/server/routers/domain/createOrgDomain.ts +++ b/server/routers/domain/createOrgDomain.ts @@ -9,8 +9,8 @@ import { fromError } from "zod-validation-error"; import { subdomainSchema } from "@server/lib/schemas"; import { generateId } from "@server/auth/sessions/app"; import { eq, and } from "drizzle-orm"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators"; import { build } from "@server/build"; import config from "@server/lib/config"; diff --git a/server/routers/domain/deleteOrgDomain.ts b/server/routers/domain/deleteOrgDomain.ts index 8932733c..8836584b 100644 --- a/server/routers/domain/deleteOrgDomain.ts +++ b/server/routers/domain/deleteOrgDomain.ts @@ -7,8 +7,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { and, eq } from "drizzle-orm"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const paramsSchema = z .object({ diff --git a/server/routers/domain/index.ts b/server/routers/domain/index.ts index e833e532..c0cafafe 100644 --- a/server/routers/domain/index.ts +++ b/server/routers/domain/index.ts @@ -1,6 +1,4 @@ export * from "./listDomains"; export * from "./createOrgDomain"; export * from "./deleteOrgDomain"; -export * from "./privateListDomainNamespaces"; -export * from "./privateCheckDomainNamespaceAvailability"; export * from "./restartOrgDomain"; \ No newline at end of file diff --git a/server/routers/external.ts b/server/routers/external.ts index 3a96bb03..d90b7478 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -38,25 +38,13 @@ import { verifyUserIsOrgOwner, verifySiteResourceAccess } from "@server/middlewares"; -import { - verifyCertificateAccess, - verifyRemoteExitNodeAccess, - verifyIdpAccess, - verifyLoginPageAccess -} from "@server/middlewares/private"; -import { createStore } from "@server/lib/private/rateLimitStore"; import { ActionsEnum } from "@server/auth/actions"; import { createNewt, getNewtToken } from "./newt"; import { getOlmToken } from "./olm"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; -import * as certificates from "./private/certificates"; -import * as billing from "@server/routers/private/billing"; -import { quickStart } from "./auth/privateQuickStart"; import { build } from "@server/build"; -import * as remoteExitNode from "@server/routers/private/remoteExitNode"; -import * as loginPage from "@server/routers/private/loginPage"; -import * as orgIdp from "@server/routers/private/orgIdp"; +import { createStore } from "#dynamic/lib/rateLimitStore"; // Root routes export const unauthenticated = Router(); @@ -65,45 +53,6 @@ unauthenticated.get("/", (_, res) => { res.status(HttpCode.OK).json({ message: "Healthy" }); }); -if (build === "saas") { - unauthenticated.post( - "/quick-start", - rateLimit({ - windowMs: 15 * 60 * 1000, - max: 100, - keyGenerator: (req) => req.path, - handler: (req, res, next) => { - const message = `We're too busy right now. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - quickStart - ); -} - -if (build !== "oss") { - unauthenticated.post( - "/remote-exit-node/quick-start", - rateLimit({ - windowMs: 60 * 60 * 1000, - max: 5, - keyGenerator: (req) => - `${req.path}:${ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only create 5 remote exit nodes every hour. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - remoteExitNode.quickStartRemoteExitNode - ); -} - // Authenticated Root routes export const authenticated = Router(); authenticated.use(verifySessionUserMiddleware); @@ -727,45 +676,7 @@ authenticated.post( idp.updateOidcIdp ); -if (build !== "oss") { - authenticated.put( - "/org/:orgId/idp/oidc", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createIdp), - orgIdp.createOrgOidcIdp - ); - authenticated.post( - "/org/:orgId/idp/:idpId/oidc", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.updateIdp), - orgIdp.updateOrgOidcIdp - ); - - authenticated.delete( - "/org/:orgId/idp/:idpId", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.deleteIdp), - idp.deleteIdp - ); - - authenticated.get( - "/org/:orgId/idp/:idpId", - verifyOrgAccess, - verifyIdpAccess, - verifyUserHasAction(ActionsEnum.getIdp), - orgIdp.getOrgIdp - ); - - authenticated.get( - "/org/:orgId/idp", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.listIdps), - orgIdp.listOrgIdps - ); -} authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp); @@ -795,9 +706,7 @@ authenticated.get( idp.listIdpOrgPolicies ); -if (build !== "oss") { - authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids -} + authenticated.get("/idp", idp.listIdps); // anyone can see this; it's just a list of idp names and ids authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); @@ -930,126 +839,6 @@ authenticated.delete( domain.deleteAccountDomain ); -if (build !== "oss") { - authenticated.get( - "/org/:orgId/certificate/:domainId/:domain", - verifyOrgAccess, - verifyCertificateAccess, - verifyUserHasAction(ActionsEnum.getCertificate), - certificates.getCertificate - ); - - authenticated.post( - "/org/:orgId/certificate/:certId/restart", - verifyOrgAccess, - verifyCertificateAccess, - verifyUserHasAction(ActionsEnum.restartCertificate), - certificates.restartCertificate - ); - - authenticated.post( - "/org/:orgId/billing/create-checkout-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createCheckoutSession - ); - - authenticated.post( - "/org/:orgId/billing/create-portal-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createPortalSession - ); - - authenticated.get( - "/org/:orgId/billing/subscription", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgSubscription - ); - - authenticated.get( - "/org/:orgId/billing/usage", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgUsage - ); - - authenticated.get("/domain/namespaces", domain.listDomainNamespaces); - - authenticated.get( - "/domain/check-namespace-availability", - domain.checkDomainNamespaceAvailability - ); - - authenticated.put( - "/org/:orgId/remote-exit-node", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createRemoteExitNode), - remoteExitNode.createRemoteExitNode - ); - - authenticated.get( - "/org/:orgId/remote-exit-nodes", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.listRemoteExitNode), - remoteExitNode.listRemoteExitNodes - ); - - authenticated.get( - "/org/:orgId/remote-exit-node/:remoteExitNodeId", - verifyOrgAccess, - verifyRemoteExitNodeAccess, - verifyUserHasAction(ActionsEnum.getRemoteExitNode), - remoteExitNode.getRemoteExitNode - ); - - authenticated.get( - "/org/:orgId/pick-remote-exit-node-defaults", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createRemoteExitNode), - remoteExitNode.pickRemoteExitNodeDefaults - ); - - authenticated.delete( - "/org/:orgId/remote-exit-node/:remoteExitNodeId", - verifyOrgAccess, - verifyRemoteExitNodeAccess, - verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), - remoteExitNode.deleteRemoteExitNode - ); - - authenticated.put( - "/org/:orgId/login-page", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.createLoginPage), - loginPage.createLoginPage - ); - - authenticated.post( - "/org/:orgId/login-page/:loginPageId", - verifyOrgAccess, - verifyLoginPageAccess, - verifyUserHasAction(ActionsEnum.updateLoginPage), - loginPage.updateLoginPage - ); - - authenticated.delete( - "/org/:orgId/login-page/:loginPageId", - verifyOrgAccess, - verifyLoginPageAccess, - verifyUserHasAction(ActionsEnum.deleteLoginPage), - loginPage.deleteLoginPage - ); - - authenticated.get( - "/org/:orgId/login-page", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.getLoginPage), - loginPage.getLoginPage - ); -} - // Auth routes export const authRouter = Router(); unauthenticated.use("/auth", authRouter); @@ -1129,26 +918,6 @@ authRouter.post( getOlmToken ); -if (build !== "oss") { - authRouter.post( - "/remoteExitNode/get-token", - rateLimit({ - windowMs: 15 * 60 * 1000, - max: 900, - keyGenerator: (req) => - `remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - remoteExitNode.getRemoteExitNodeToken - ); -} - authRouter.post( "/2fa/enable", rateLimit({ @@ -1316,26 +1085,6 @@ authRouter.post( resource.authWithWhitelist ); -if (build !== "oss") { - authRouter.post( - "/transfer-session-token", - rateLimit({ - windowMs: 1 * 60 * 1000, - max: 60, - keyGenerator: (req) => - `transferSessionToken:${ipKeyGenerator(req.ip || "")}`, - handler: (req, res, next) => { - const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`; - return next( - createHttpError(HttpCode.TOO_MANY_REQUESTS, message) - ); - }, - store: createStore() - }), - auth.transferSession - ); -} - authRouter.post( "/resource/:resourceId/access-token", resource.authWithAccessToken diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index afae4009..1604dc30 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -1,19 +1,16 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { sites, resources, targets, exitNodes, ExitNode } from "@server/db"; +import { sites, exitNodes, ExitNode } from "@server/db"; import { db } from "@server/db"; import { eq, isNotNull, and } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import config from "@server/lib/config"; -import { getUniqueExitNodeEndpointName } from "../../db/names"; -import { findNextAvailableCidr } from "@server/lib/ip"; import { fromError } from "zod-validation-error"; import { getAllowedIps } from "../target/helpers"; import { proxyToRemote } from "@server/lib/remoteProxy"; -import { getNextAvailableSubnet } from "@server/lib/exitNodes"; -import { createExitNode } from "./privateCreateExitNode"; +import { createExitNode } from "#dynamic/routers/gerbil/createExitNode"; // Define Zod schema for request validation const getConfigSchema = z.object({ diff --git a/server/routers/gerbil/getResolvedHostname.ts b/server/routers/gerbil/getResolvedHostname.ts index 17067c55..f06cd1b8 100644 --- a/server/routers/gerbil/getResolvedHostname.ts +++ b/server/routers/gerbil/getResolvedHostname.ts @@ -4,9 +4,9 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { resolveExitNodes } from "@server/lib/exitNodes"; -import config from "@server/lib/config"; +import { resolveExitNodes } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; +import config from "@server/lib/config"; // Define Zod schema for request validation const getResolvedHostnameSchema = z.object({ @@ -36,7 +36,15 @@ export async function getResolvedHostname( const { hostname, publicKey } = parsedParams.data; - const baseDomain = config.getRawPrivateConfig().app.base_domain; + const dashboardUrl = config.getRawConfig().app.dashboard_url; + + // extract the domain removing the http and stuff + const baseDomain = dashboardUrl + ? dashboardUrl + .replace("http://", "") + .replace("https://", "") + .split("/")[0] + : null; // if the hostname ends with the base domain then send back a empty array if (baseDomain && hostname.endsWith(baseDomain)) { @@ -50,6 +58,13 @@ export async function getResolvedHostname( publicKey ); + if (resourceExitNodes.length === 0) { + // no exit nodes found, return empty array to force local routing + return res.status(HttpCode.OK).send({ + endpoints: [] // this should force to route locally + }); + } + endpoints = resourceExitNodes.map((node) => node.endpoint); } diff --git a/server/routers/gerbil/peers.ts b/server/routers/gerbil/peers.ts index 1cdc9184..44af7fbd 100644 --- a/server/routers/gerbil/peers.ts +++ b/server/routers/gerbil/peers.ts @@ -2,7 +2,7 @@ import logger from "@server/logger"; import { db } from "@server/db"; import { exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; export async function addPeer( exitNodeId: number, diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index d10141b9..3661dedd 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -6,9 +6,9 @@ import logger from "@server/logger"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing/features"; -import { checkExitNodeOrg } from "@server/lib/exitNodes"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing/features"; +import { checkExitNodeOrg } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; // Track sites that are already offline to avoid unnecessary queries diff --git a/server/routers/gerbil/updateHolePunch.ts b/server/routers/gerbil/updateHolePunch.ts index 65217178..34bc2c6b 100644 --- a/server/routers/gerbil/updateHolePunch.ts +++ b/server/routers/gerbil/updateHolePunch.ts @@ -18,8 +18,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; import { validateOlmSessionToken } from "@server/auth/sessions/olm"; -import axios from "axios"; -import { checkExitNodeOrg } from "@server/lib/exitNodes"; +import { checkExitNodeOrg } from "#dynamic/lib/exitNodes"; // Define Zod schema for request validation const updateHolePunchSchema = z.object({ diff --git a/server/routers/hybrid.ts b/server/routers/hybrid.ts new file mode 100644 index 00000000..235961f1 --- /dev/null +++ b/server/routers/hybrid.ts @@ -0,0 +1,4 @@ +import { Router } from "express"; + +// Root routes +export const hybridRouter = Router(); \ No newline at end of file diff --git a/server/routers/idp/generateOidcUrl.ts b/server/routers/idp/generateOidcUrl.ts index 90144816..3c81ce0b 100644 --- a/server/routers/idp/generateOidcUrl.ts +++ b/server/routers/idp/generateOidcUrl.ts @@ -14,8 +14,8 @@ import jsonwebtoken from "jsonwebtoken"; import config from "@server/lib/config"; import { decrypt } from "@server/lib/crypto"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index fec21e41..0ecf08b0 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -30,8 +30,8 @@ import { } from "@server/auth/sessions/app"; import { decrypt } from "@server/lib/crypto"; import { UserType } from "@server/types/UserTypes"; -import { FeatureId } from "@server/lib/private/billing"; -import { usageService } from "@server/lib/private/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; const ensureTrailingSlash = (url: string): string => { return url; diff --git a/server/routers/integration.ts b/server/routers/integration.ts index 879075c7..d0c7c5d5 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -555,13 +555,6 @@ authenticated.post( idp.updateOidcIdp ); -authenticated.delete( - "/idp/:idpId", - verifyApiKeyIsRoot, - verifyApiKeyHasAction(ActionsEnum.deleteIdp), - idp.deleteIdp -); - authenticated.get( "/idp", verifyApiKeyIsRoot, @@ -604,15 +597,6 @@ authenticated.get( idp.listIdpOrgPolicies ); -if (build == "saas") { - authenticated.post( - `/org/:orgId/send-usage-notification`, - verifyApiKeyIsRoot, // We are the only ones who can use root key so its fine - verifyApiKeyHasAction(ActionsEnum.sendUsageNotification), - org.sendUsageNotification - ); -} - authenticated.get( "/org/:orgId/pick-client-defaults", verifyClientsEnabled, diff --git a/server/routers/internal.ts b/server/routers/internal.ts index e4525118..10966bb5 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -7,7 +7,6 @@ import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; import * as license from "@server/routers/license"; import * as idp from "@server/routers/idp"; -import * as loginPage from "@server/routers/private/loginPage"; import { proxyToRemote } from "@server/lib/remoteProxy"; import config from "@server/lib/config"; import HttpCode from "@server/types/HttpCode"; @@ -15,12 +14,9 @@ import { verifyResourceAccess, verifySessionUserMiddleware } from "@server/middlewares"; -import { build } from "@server/build"; -import * as billing from "@server/routers/private/billing"; -import * as orgIdp from "@server/routers/private/orgIdp"; // Root routes -const internalRouter = Router(); +export const internalRouter = Router(); internalRouter.get("/", (_, res) => { res.status(HttpCode.OK).json({ message: "Healthy" }); @@ -51,12 +47,6 @@ internalRouter.get("/idp", idp.listIdps); internalRouter.get("/idp/:idpId", idp.getIdp); -if (build !== "oss") { - internalRouter.get("/org/:orgId/idp", orgIdp.listOrgIdps); - - internalRouter.get("/org/:orgId/billing/tier", billing.getOrgTier); -} - // Gerbil routes const gerbilRouter = Router(); internalRouter.use("/gerbil", gerbilRouter); @@ -106,16 +96,4 @@ if (config.isManagedMode()) { ); } else { badgerRouter.post("/exchange-session", badger.exchangeSession); -} - -if (build !== "oss") { - internalRouter.get("/login-page", loginPage.loadLoginPage); - - internalRouter.post( - "/get-session-transfer-token", - verifySessionUserMiddleware, - auth.getSessionTransferToken - ); -} - -export default internalRouter; +} \ No newline at end of file diff --git a/server/routers/newt/dockerSocket.ts b/server/routers/newt/dockerSocket.ts index 0c59d354..3847d9a4 100644 --- a/server/routers/newt/dockerSocket.ts +++ b/server/routers/newt/dockerSocket.ts @@ -1,5 +1,5 @@ import NodeCache from "node-cache"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; export const dockerSocketCache = new NodeCache({ stdTTL: 3600 // seconds diff --git a/server/routers/newt/handleApplyBlueprintMessage.ts b/server/routers/newt/handleApplyBlueprintMessage.ts index 68158799..62802fff 100644 --- a/server/routers/newt/handleApplyBlueprintMessage.ts +++ b/server/routers/newt/handleApplyBlueprintMessage.ts @@ -1,5 +1,5 @@ import { db, newts } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { eq, and, sql, inArray } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/newt/handleGetConfigMessage.ts b/server/routers/newt/handleGetConfigMessage.ts index 2b65fd06..3eba94b9 100644 --- a/server/routers/newt/handleGetConfigMessage.ts +++ b/server/routers/newt/handleGetConfigMessage.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { @@ -14,7 +14,7 @@ import { import { clients, clientSites, Newt, sites } from "@server/db"; import { eq, and, inArray } from "drizzle-orm"; import { updatePeer } from "../olm/peers"; -import { sendToExitNode } from "@server/lib/exitNodes"; +import { sendToExitNode } from "#dynamic/lib/exitNodes"; const inputSchema = z.object({ publicKey: z.string(), diff --git a/server/routers/newt/handleNewtPingRequestMessage.ts b/server/routers/newt/handleNewtPingRequestMessage.ts index aeb7a155..fea157fd 100644 --- a/server/routers/newt/handleNewtPingRequestMessage.ts +++ b/server/routers/newt/handleNewtPingRequestMessage.ts @@ -1,10 +1,9 @@ import { db, sites } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt } from "@server/db"; import logger from "@server/logger"; -import config from "@server/lib/config"; import { ne, eq, or, and, count } from "drizzle-orm"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export const handleNewtPingRequestMessage: MessageHandler = async (context) => { const { message, client, sendToClient } = context; diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 021527ad..372f3677 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -1,5 +1,5 @@ import { db, exitNodeOrgs, newts } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { targetHealthCheck } from "@server/db"; import { eq, and, sql, inArray } from "drizzle-orm"; @@ -10,12 +10,12 @@ import { findNextAvailableCidr, getNextAvailableClientSubnet } from "@server/lib/ip"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { selectBestExitNode, verifyExitNodeOrgAccess -} from "@server/lib/exitNodes"; +} from "#dynamic/lib/exitNodes"; import { fetchContainers } from "./dockerSocket"; export type ExitNodePingResult = { diff --git a/server/routers/newt/handleReceiveBandwidthMessage.ts b/server/routers/newt/handleReceiveBandwidthMessage.ts index 89b24f78..f5170feb 100644 --- a/server/routers/newt/handleReceiveBandwidthMessage.ts +++ b/server/routers/newt/handleReceiveBandwidthMessage.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, Newt } from "@server/db"; import { eq } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/newt/handleSocketMessages.ts b/server/routers/newt/handleSocketMessages.ts index aceca37d..0491393f 100644 --- a/server/routers/newt/handleSocketMessages.ts +++ b/server/routers/newt/handleSocketMessages.ts @@ -1,4 +1,4 @@ -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import logger from "@server/logger"; import { dockerSocketCache } from "./dockerSocket"; import { Newt } from "@server/db"; diff --git a/server/routers/newt/peers.ts b/server/routers/newt/peers.ts index ff57e6fd..03dc3460 100644 --- a/server/routers/newt/peers.ts +++ b/server/routers/newt/peers.ts @@ -1,7 +1,7 @@ import { db } from "@server/db"; import { newts, sites } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; export async function addPeer( diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index a886b00b..97e4030d 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -1,5 +1,5 @@ import { Target, TargetHealthCheck, db, targetHealthCheck } from "@server/db"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; import { eq, inArray } from "drizzle-orm"; diff --git a/server/routers/olm/handleOlmPingMessage.ts b/server/routers/olm/handleOlmPingMessage.ts index 6c4b5600..6f00640d 100644 --- a/server/routers/olm/handleOlmPingMessage.ts +++ b/server/routers/olm/handleOlmPingMessage.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, Olm } from "@server/db"; import { eq, lt, isNull, and, or } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/olm/handleOlmRegisterMessage.ts b/server/routers/olm/handleOlmRegisterMessage.ts index 33d7f9cb..66128f0e 100644 --- a/server/routers/olm/handleOlmRegisterMessage.ts +++ b/server/routers/olm/handleOlmRegisterMessage.ts @@ -1,10 +1,10 @@ import { db, ExitNode } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, clientSites, exitNodes, Olm, olms, sites } from "@server/db"; import { and, eq, inArray } from "drizzle-orm"; import { addPeer, deletePeer } from "../newt/peers"; import logger from "@server/logger"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export const handleOlmRegisterMessage: MessageHandler = async (context) => { logger.info("Handling register olm message!"); diff --git a/server/routers/olm/handleOlmRelayMessage.ts b/server/routers/olm/handleOlmRelayMessage.ts index cefc5b91..9b31754c 100644 --- a/server/routers/olm/handleOlmRelayMessage.ts +++ b/server/routers/olm/handleOlmRelayMessage.ts @@ -1,5 +1,5 @@ import { db, exitNodes, sites } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { clients, clientSites, Olm } from "@server/db"; import { and, eq } from "drizzle-orm"; import { updatePeer } from "../newt/peers"; diff --git a/server/routers/olm/peers.ts b/server/routers/olm/peers.ts index a8d6be9c..396866a1 100644 --- a/server/routers/olm/peers.ts +++ b/server/routers/olm/peers.ts @@ -1,7 +1,7 @@ import { db } from "@server/db"; import { clients, olms, newts, sites } from "@server/db"; import { eq } from "drizzle-orm"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import logger from "@server/logger"; export async function addPeer( diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index a12520a4..d8bcb9da 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -3,8 +3,6 @@ import { z } from "zod"; import { db } from "@server/db"; import { eq } from "drizzle-orm"; import { - apiKeyOrg, - apiKeys, domains, Org, orgDomains, @@ -24,9 +22,9 @@ import { fromError } from "zod-validation-error"; import { defaultRoleAllowedActions } from "../role"; import { OpenAPITags, registry } from "@server/openApi"; import { isValidCIDR } from "@server/lib/validators"; -import { createCustomer } from "@server/routers/private/billing/createCustomer"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { createCustomer } from "#dynamic/lib/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; const createOrgSchema = z diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index 63e9abb0..8a424e5b 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { deletePeer } from "../gerbil/peers"; import { OpenAPITags, registry } from "@server/openApi"; diff --git a/server/routers/org/index.ts b/server/routers/org/index.ts index 013f6c6d..7887fcac 100644 --- a/server/routers/org/index.ts +++ b/server/routers/org/index.ts @@ -7,5 +7,4 @@ export * from "./checkId"; export * from "./getOrgOverview"; export * from "./listOrgs"; export * from "./pickOrgDefaults"; -export * from "./privateSendUsageNotifications"; export * from "./applyBlueprint"; diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 53af0b72..f4fe352e 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -21,7 +21,7 @@ import { subdomainSchema } from "@server/lib/schemas"; import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; import { build } from "@server/build"; -import { createCertificate } from "../private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { getUniqueResourceName } from "@server/db/names"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 83fcf6f1..a9c3b5de 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -20,7 +20,7 @@ import { tlsNameSchema } from "@server/lib/schemas"; import { subdomainSchema } from "@server/lib/schemas"; import { registry } from "@server/openApi"; import { OpenAPITags } from "@server/openApi"; -import { createCertificate } from "../private/certificates/createCertificate"; +import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { validateHeaders } from "@server/lib/validators"; import { build } from "@server/build"; diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 5ffa6954..0ffc5956 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -16,8 +16,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import { hashPassword } from "@server/auth/password"; import { isValidIP } from "@server/lib/validators"; import { isIpInCidr } from "@server/lib/ip"; -import config from "@server/lib/config"; -import { verifyExitNodeOrgAccess } from "@server/lib/exitNodes"; +import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes"; const createSiteParamsSchema = z .object({ diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 4af2feae..7a12e24a 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { deletePeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { OpenAPITags, registry } from "@server/openApi"; const deleteSiteSchema = z diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 46d3c53b..c4b3a087 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -15,7 +15,7 @@ import config from "@server/lib/config"; import { OpenAPITags, registry } from "@server/openApi"; import { fromError } from "zod-validation-error"; import { z } from "zod"; -import { listExitNodes } from "@server/lib/exitNodes"; +import { listExitNodes } from "#dynamic/lib/exitNodes"; export type PickSiteDefaultsResponse = { exitNodeId: number; diff --git a/server/routers/site/socketIntegration.ts b/server/routers/site/socketIntegration.ts index 34084a0a..7b5160cb 100644 --- a/server/routers/site/socketIntegration.ts +++ b/server/routers/site/socketIntegration.ts @@ -9,7 +9,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import stoi from "@server/lib/stoi"; -import { sendToClient } from "../ws"; +import { sendToClient } from "#dynamic/routers/ws"; import { fetchContainers, dockerSocketCache, diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index 0e958889..317f6461 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -3,6 +3,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; +import privateConfig from "#private/lib/config"; import config from "@server/lib/config"; import { db } from "@server/db"; import { count } from "drizzle-orm"; @@ -45,7 +46,7 @@ export async function isSupporterKeyVisible( } } - if (config.getRawPrivateConfig().flags?.hide_supporter_key && build != "oss") { + if (build != "oss") { visible = false; } diff --git a/server/routers/target/handleHealthcheckStatusMessage.ts b/server/routers/target/handleHealthcheckStatusMessage.ts index d726b5af..ee4e7950 100644 --- a/server/routers/target/handleHealthcheckStatusMessage.ts +++ b/server/routers/target/handleHealthcheckStatusMessage.ts @@ -1,5 +1,5 @@ import { db, targets, resources, sites, targetHealthCheck } from "@server/db"; -import { MessageHandler } from "../ws"; +import { MessageHandler } from "@server/routers/ws"; import { Newt } from "@server/db"; import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/routers/traefik/traefikConfigProvider.ts b/server/routers/traefik/traefikConfigProvider.ts index 50ec0770..6c9404e9 100644 --- a/server/routers/traefik/traefikConfigProvider.ts +++ b/server/routers/traefik/traefikConfigProvider.ts @@ -3,7 +3,7 @@ import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { build } from "@server/build"; -import { getTraefikConfig } from "@server/lib/traefik"; +import { getTraefikConfig } from "#dynamic/lib/traefik"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; const badgerMiddlewareName = "badger"; diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index b553bd19..5e4264f9 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -10,8 +10,8 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { checkValidInvite } from "@server/auth/checkValidInvite"; import { verifySession } from "@server/auth/sessions/verifySession"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; const acceptInviteBodySchema = z .object({ diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index b8f681c3..29f94641 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -10,11 +10,11 @@ import { db, UserOrg } from "@server/db"; import { and, eq } from "drizzle-orm"; import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db"; import { generateId } from "@server/auth/sessions/app"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; -import { getOrgTierData } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { getOrgTierData } from "#dynamic/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; const paramsSchema = z .object({ diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 746a383b..56098bea 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -17,8 +17,8 @@ import { sendEmail } from "@server/emails"; import SendInviteLink from "@server/emails/templates/SendInviteLink"; import { OpenAPITags, registry } from "@server/openApi"; import { UserType } from "@server/types/UserTypes"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; const regenerateTracker = new NodeCache({ stdTTL: 3600, checkperiod: 600 }); diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index 6d0c5359..babccdd0 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -9,8 +9,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; -import { usageService } from "@server/lib/private/billing/usageService"; -import { FeatureId } from "@server/lib/private/billing"; +import { usageService } from "@server/lib/billing/usageService"; +import { FeatureId } from "@server/lib/billing"; import { build } from "@server/build"; import { UserType } from "@server/types/UserTypes"; diff --git a/server/routers/ws/index.ts b/server/routers/ws/index.ts index 376a960b..16440ec9 100644 --- a/server/routers/ws/index.ts +++ b/server/routers/ws/index.ts @@ -1,24 +1,2 @@ -import { build } from "@server/build"; - -// Import both modules -import * as wsModule from "./ws"; -import * as privateWsModule from "./privateWs"; - -// Conditionally export WebSocket implementation based on build type -const wsImplementation = build === "oss" ? wsModule : privateWsModule; - -// Re-export all items from the selected implementation -export const { - router, - handleWSUpgrade, - sendToClient, - broadcastToAllExcept, - connectedClients, - hasActiveConnections, - getActiveNodes, - NODE_ID, - cleanup -} = wsImplementation; - -// Re-export the MessageHandler type (both modules have the same type signature) -export type { MessageHandler } from "./privateWs"; \ No newline at end of file +export * from "./ws"; +export * from "./types"; \ No newline at end of file diff --git a/server/routers/ws/messageHandlers.ts b/server/routers/ws/messageHandlers.ts index 5b111eec..cbb023b3 100644 --- a/server/routers/ws/messageHandlers.ts +++ b/server/routers/ws/messageHandlers.ts @@ -13,10 +13,8 @@ import { handleOlmPingMessage, startOlmOfflineChecker } from "../olm"; -import { handleRemoteExitNodeRegisterMessage, handleRemoteExitNodePingMessage, startRemoteExitNodeOfflineChecker } from "@server/routers/private/remoteExitNode"; -import { MessageHandler } from "./privateWs"; import { handleHealthcheckStatusMessage } from "../target"; -import { build } from "@server/build"; +import { MessageHandler } from "./types"; export const messageHandlers: Record = { "newt/wg/register": handleNewtRegisterMessage, @@ -30,12 +28,6 @@ export const messageHandlers: Record = { "newt/ping/request": handleNewtPingRequestMessage, "newt/blueprint/apply": handleApplyBlueprintMessage, "newt/healthcheck/status": handleHealthcheckStatusMessage, - - "remoteExitNode/register": handleRemoteExitNodeRegisterMessage, - "remoteExitNode/ping": handleRemoteExitNodePingMessage, }; -startOlmOfflineChecker(); // this is to handle the offline check for olms -if (build != "oss") { - startRemoteExitNodeOfflineChecker(); // this is to handle the offline check for remote exit nodes -} \ No newline at end of file +startOlmOfflineChecker(); // this is to handle the offline check for olms \ No newline at end of file diff --git a/server/routers/ws/types.ts b/server/routers/ws/types.ts new file mode 100644 index 00000000..7063bc87 --- /dev/null +++ b/server/routers/ws/types.ts @@ -0,0 +1,70 @@ +import { + Newt, + newts, + NewtSession, + olms, + Olm, + OlmSession, + RemoteExitNode, + RemoteExitNodeSession, + remoteExitNodes +} from "@server/db"; +import { IncomingMessage } from "http"; +import { WebSocket } from "ws"; + +// Custom interfaces +export interface WebSocketRequest extends IncomingMessage { + token?: string; +} + +export type ClientType = "newt" | "olm" | "remoteExitNode"; + +export interface AuthenticatedWebSocket extends WebSocket { + client?: Newt | Olm | RemoteExitNode; + clientType?: ClientType; + connectionId?: string; + isFullyConnected?: boolean; + pendingMessages?: Buffer[]; +} + +export interface TokenPayload { + client: Newt | Olm | RemoteExitNode; + session: NewtSession | OlmSession | RemoteExitNodeSession; + clientType: ClientType; +} + +export interface WSMessage { + type: string; + data: any; +} + +export interface HandlerResponse { + message: WSMessage; + broadcast?: boolean; + excludeSender?: boolean; + targetClientId?: string; +} + +export interface HandlerContext { + message: WSMessage; + senderWs: WebSocket; + client: Newt | Olm | RemoteExitNode | undefined; + clientType: ClientType; + sendToClient: (clientId: string, message: WSMessage) => Promise; + broadcastToAllExcept: ( + message: WSMessage, + excludeClientId?: string + ) => Promise; + connectedClients: Map; +} + +export type MessageHandler = (context: HandlerContext) => Promise; + +// Redis message type for cross-node communication +export interface RedisMessage { + type: "direct" | "broadcast"; + targetClientId?: string; + excludeClientId?: string; + message: WSMessage; + fromNodeId: string; +} \ No newline at end of file diff --git a/server/routers/ws/ws.ts b/server/routers/ws/ws.ts index 8fb773d3..9bba41dc 100644 --- a/server/routers/ws/ws.ts +++ b/server/routers/ws/ws.ts @@ -1,7 +1,6 @@ import { Router, Request, Response } from "express"; import { Server as HttpServer } from "http"; import { WebSocket, WebSocketServer } from "ws"; -import { IncomingMessage } from "http"; import { Socket } from "net"; import { Newt, newts, NewtSession, olms, Olm, OlmSession } from "@server/db"; import { eq } from "drizzle-orm"; @@ -11,50 +10,15 @@ import { validateOlmSessionToken } from "@server/auth/sessions/olm"; import { messageHandlers } from "./messageHandlers"; import logger from "@server/logger"; import { v4 as uuidv4 } from "uuid"; +import { ClientType, TokenPayload, WebSocketRequest, WSMessage, AuthenticatedWebSocket } from "./types"; -// Custom interfaces -interface WebSocketRequest extends IncomingMessage { - token?: string; -} - -type ClientType = 'newt' | 'olm'; - -interface AuthenticatedWebSocket extends WebSocket { - client?: Newt | Olm; - clientType?: ClientType; - connectionId?: string; -} - -interface TokenPayload { +// Subset of TokenPayload for public ws.ts (newt and olm only) +interface PublicTokenPayload { client: Newt | Olm; session: NewtSession | OlmSession; - clientType: ClientType; + clientType: "newt" | "olm"; } -interface WSMessage { - type: string; - data: any; -} - -interface HandlerResponse { - message: WSMessage; - broadcast?: boolean; - excludeSender?: boolean; - targetClientId?: string; -} - -interface HandlerContext { - message: WSMessage; - senderWs: WebSocket; - client: Newt | Olm | undefined; - clientType: ClientType; - sendToClient: (clientId: string, message: WSMessage) => Promise; - broadcastToAllExcept: (message: WSMessage, excludeClientId?: string) => Promise; - connectedClients: Map; -} - -export type MessageHandler = (context: HandlerContext) => Promise; - const router: Router = Router(); const wss: WebSocketServer = new WebSocketServer({ noServer: true }); @@ -153,7 +117,7 @@ const getActiveNodes = async (clientType: ClientType, clientId: string): Promise }; // Token verification middleware -const verifyToken = async (token: string, clientType: ClientType): Promise => { +const verifyToken = async (token: string, clientType: ClientType): Promise => { try { if (clientType === 'newt') { @@ -169,7 +133,7 @@ try { return null; } return { client: existingNewt[0], session, clientType }; - } else { + } else if (clientType === 'olm') { const { session, olm } = await validateOlmSessionToken(token); if (!session || !olm) { return null; @@ -183,13 +147,15 @@ try { } return { client: existingOlm[0], session, clientType }; } + + return null; } catch (error) { logger.error("Token verification failed:", error); return null; } }; -const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, clientType: ClientType): Promise => { +const setupConnection = async (ws: AuthenticatedWebSocket, client: Newt | Olm, clientType: "newt" | "olm"): Promise => { logger.info("Establishing websocket connection"); if (!client) { logger.error("Connection attempt without client"); @@ -323,10 +289,6 @@ const cleanup = async (): Promise => { } }; -// Handle process termination -process.on('SIGTERM', cleanup); -process.on('SIGINT', cleanup); - export { router, handleWSUpgrade, diff --git a/src/app/[orgId]/layout.tsx b/src/app/[orgId]/layout.tsx index ecc7de01..d8f28a59 100644 --- a/src/app/[orgId]/layout.tsx +++ b/src/app/[orgId]/layout.tsx @@ -9,8 +9,8 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; import SetLastOrgCookie from "@app/components/SetLastOrgCookie"; -import PrivateSubscriptionStatusProvider from "@app/providers/PrivateSubscriptionStatusProvider"; -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing/getOrgSubscription"; +import PrivateSubscriptionStatusProvider from "@app/providers/SubscriptionStatusProvider"; +import { GetOrgSubscriptionResponse } from "#private/routers/billing/getOrgSubscription"; import { pullEnv } from "@app/lib/pullEnv"; import { build } from "@server/build"; diff --git a/src/app/[orgId]/settings/(private)/billing/layout.tsx b/src/app/[orgId]/settings/(private)/billing/layout.tsx index 8559ddf4..3bb60caf 100644 --- a/src/app/[orgId]/settings/(private)/billing/layout.tsx +++ b/src/app/[orgId]/settings/(private)/billing/layout.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; diff --git a/src/app/[orgId]/settings/(private)/billing/page.tsx b/src/app/[orgId]/settings/(private)/billing/page.tsx index 1270c30f..cab22311 100644 --- a/src/app/[orgId]/settings/(private)/billing/page.tsx +++ b/src/app/[orgId]/settings/(private)/billing/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@app/components/ui/button"; @@ -50,7 +37,7 @@ import { InfoPopup } from "@/components/ui/info-popup"; import { GetOrgSubscriptionResponse, GetOrgUsageResponse -} from "@server/routers/private/billing"; +} from "#private/routers/billing"; import { useTranslations } from "use-intl"; import Link from "next/link"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx index d4bfdcd0..a5be3c83 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx index 4846dd56..7cdea07a 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { GetIdpResponse as GetOrgIdpResponse } from "@server/routers/idp"; import { AxiosResponse } from "axios"; diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx index 1d53035c..ecc2aa83 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { redirect } from "next/navigation"; export default async function IdpPage(props: { diff --git a/src/app/[orgId]/settings/(private)/idp/create/page.tsx b/src/app/[orgId]/settings/(private)/idp/create/page.tsx index 03f83afe..ba580ca0 100644 --- a/src/app/[orgId]/settings/(private)/idp/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/create/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { diff --git a/src/app/[orgId]/settings/(private)/idp/page.tsx b/src/app/[orgId]/settings/(private)/idp/page.tsx index 1032e66c..2d1882b9 100644 --- a/src/app/[orgId]/settings/(private)/idp/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal, priv } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; import { AxiosResponse } from "axios"; @@ -22,8 +9,8 @@ import { cache } from "react"; import { GetOrgSubscriptionResponse, GetOrgTierResponse -} from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +} from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; type OrgIdpPageProps = { diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx index 4a1db4ea..a1bb69c0 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesDataTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx index 11e0bcfc..c7e332d7 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/ExitNodesTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx index b5835e1b..191ce3f3 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/general/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - export default function GeneralPage() { return <>; } diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx index 653444e8..bfab3086 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx @@ -1,24 +1,11 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { authCookieHeader } from "@app/lib/api/cookies"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { getTranslations } from "next-intl/server"; -import RemoteExitNodeProvider from "@app/providers/PrivateRemoteExitNodeProvider"; +import RemoteExitNodeProvider from "@app/providers/RemoteExitNodeProvider"; interface SettingsLayoutProps { children: React.ReactNode; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx index badf0971..6b39c1de 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { redirect } from "next/navigation"; export default async function RemoteExitNodePage(props: { diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx index 5daaa493..7531ba6d 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { @@ -43,7 +30,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { QuickStartRemoteExitNodeResponse, PickRemoteExitNodeDefaultsResponse -} from "@server/routers/private/remoteExitNode"; +} from "#private/routers/remoteExitNode"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter, useSearchParams } from "next/navigation"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx index b18df692..c4113558 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx @@ -1,19 +1,6 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; -import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; import { AxiosResponse } from "axios"; import ExitNodesTable, { RemoteExitNodeRow } from "./ExitNodesTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 3e6d2458..2b6fddad 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -47,8 +47,8 @@ import { ListIdpsResponse } from "@server/routers/idp"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; import Image from "next/image"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; type UserType = "internal" | "oidc"; @@ -76,7 +76,7 @@ export default function Page() { const api = createApiClient({ env }); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; const [selectedOption, setSelectedOption] = useState("internal"); diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index 31cfbc5d..6a088196 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -60,8 +60,8 @@ import { } from "@app/components/ui/select"; import { Separator } from "@app/components/ui/separator"; import { build } from "@server/build"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; const UsersRolesFormSchema = z.object({ roles: z.array( @@ -98,7 +98,7 @@ export default function ResourceAuthenticationPage() { const router = useRouter(); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; const [pageLoading, setPageLoading] = useState(true); diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 78fbfc0d..73ead11f 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -53,7 +53,7 @@ import { CreateSiteResponse, PickSiteDefaultsResponse } from "@server/routers/site"; -import { ListRemoteExitNodesResponse } from "@server/routers/private/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter } from "next/navigation"; diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index 0cbe101b..c438ba66 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { formatAxiosError, priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { authCookieHeader } from "@app/lib/api/cookies"; @@ -19,13 +6,13 @@ import { verifySession } from "@app/lib/auth/verifySession"; import { redirect } from "next/navigation"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; -import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; +import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; import { build } from "@server/build"; import { headers } from "next/headers"; import { GetLoginPageResponse, LoadLoginPageResponse -} from "@server/routers/private/loginPage"; +} from "#private/routers/loginPage"; import IdpLoginButtons from "@app/components/private/IdpLoginButtons"; import { Card, @@ -37,11 +24,10 @@ import { import { Button } from "@app/components/ui/button"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; -import { GetSessionTransferTokenRenponse } from "@server/routers/auth/privateGetSessionTransferToken"; -import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; +import { GetSessionTransferTokenRenponse } from "#private/routers/auth/getSessionTransferToken"; import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken"; -import { GetOrgTierResponse } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { GetOrgTierResponse } from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 5dfa72c3..6c503da8 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -6,7 +6,7 @@ import { AxiosResponse } from "axios"; import { GetIdpResponse } from "@server/routers/idp"; import { getTranslations } from "next-intl/server"; import { pullEnv } from "@app/lib/pullEnv"; -import { LoadLoginPageResponse } from "@server/routers/private/loginPage"; +import { LoadLoginPageResponse } from "#private/routers/loginPage"; import { redirect } from "next/navigation"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index 53efef18..d37bc8ca 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -15,13 +15,13 @@ import AccessToken from "@app/components/AccessToken"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; import { ListIdpsResponse } from "@server/routers/idp"; -import { ListOrgIdpsResponse } from "@server/routers/private/orgIdp"; +import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; import AutoLoginHandler from "@app/components/AutoLoginHandler"; import { build } from "@server/build"; import { headers } from "next/headers"; -import { GetLoginPageResponse } from "@server/routers/private/loginPage"; -import { GetOrgTierResponse } from "@server/routers/private/billing"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { GetLoginPageResponse } from "#private/routers/loginPage"; +import { GetOrgTierResponse } from "#private/routers/billing"; +import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 48170da5..29d49300 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,7 +4,7 @@ import { Inter } from "next/font/google"; import { ThemeProvider } from "@app/providers/ThemeProvider"; import EnvProvider from "@app/providers/EnvProvider"; import { pullEnv } from "@app/lib/pullEnv"; -import ThemeDataProvider from "@app/providers/PrivateThemeDataProvider"; +import ThemeDataProvider from "@app/providers/ThemeDataProvider"; import SplashImage from "@app/components/private/SplashImage"; import SupportStatusProvider from "@app/providers/SupporterStatusProvider"; import { priv } from "@app/lib/api"; diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index dccef529..20c9960c 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -32,7 +32,7 @@ import { createApiClient, formatAxiosError } from "@/lib/api"; import { useEnvContext } from "@/hooks/useEnvContext"; import { toast } from "@/hooks/useToast"; import { ListDomainsResponse } from "@server/routers/domain/listDomains"; -import { CheckDomainAvailabilityResponse } from "@server/routers/domain/privateCheckDomainNamespaceAvailability"; +import { CheckDomainAvailabilityResponse } from "#private/routers/domain/checkDomainNamespaceAvailability"; import { AxiosResponse } from "axios"; import { cn } from "@/lib/cn"; import { useTranslations } from "next-intl"; diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 1917c609..46c5ede0 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@app/components/ui/button"; import { useOrgContext } from "@app/hooks/useOrgContext"; @@ -43,7 +30,7 @@ import { SettingsSectionForm } from "@app/components/Settings"; import { useTranslations } from "next-intl"; -import { GetLoginPageResponse } from "@server/routers/private/loginPage"; +import { GetLoginPageResponse } from "#private/routers/loginPage"; import { ListDomainsResponse } from "@server/routers/domain"; import { DomainRow } from "@app/components/DomainsTable"; import { toUnicode } from "punycode"; @@ -63,8 +50,8 @@ import DomainPicker from "@app/components/DomainPicker"; import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; import { InfoPopup } from "@app/components/ui/info-popup"; import { Alert, AlertDescription } from "@app/components/ui/alert"; -import { usePrivateSubscriptionStatusContext } from "@app/hooks/privateUseSubscriptionStatusContext"; -import { TierId } from "@server/lib/private/billing/tiers"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; +import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; // Auth page form schema @@ -94,7 +81,7 @@ const AuthPageSettings = forwardRef( const router = useRouter(); const t = useTranslations(); - const subscription = usePrivateSubscriptionStatusContext(); + const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; // Auth page domain state diff --git a/src/components/private/AutoProvisionConfigWidget.tsx b/src/components/private/AutoProvisionConfigWidget.tsx index 35800ccc..159ba01e 100644 --- a/src/components/private/AutoProvisionConfigWidget.tsx +++ b/src/components/private/AutoProvisionConfigWidget.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { diff --git a/src/components/private/CertificateStatus.tsx b/src/components/private/CertificateStatus.tsx index 1b872371..f1446806 100644 --- a/src/components/private/CertificateStatus.tsx +++ b/src/components/private/CertificateStatus.tsx @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { Button } from "@/components/ui/button"; import { RotateCw } from "lucide-react"; -import { useCertificate } from "@app/hooks/privateUseCertificate"; +import { useCertificate } from "@app/hooks/useCertificate"; import { useTranslations } from "next-intl"; type CertificateStatusProps = { diff --git a/src/components/private/IdpLoginButtons.tsx b/src/components/private/IdpLoginButtons.tsx index 6b3a2e0b..c2ec1f5b 100644 --- a/src/components/private/IdpLoginButtons.tsx +++ b/src/components/private/IdpLoginButtons.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEffect, useState } from "react"; diff --git a/src/components/private/OrgIdpDataTable.tsx b/src/components/private/OrgIdpDataTable.tsx index c98a6234..a7dc1850 100644 --- a/src/components/private/OrgIdpDataTable.tsx +++ b/src/components/private/OrgIdpDataTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/components/private/OrgIdpTable.tsx b/src/components/private/OrgIdpTable.tsx index f0e4d6c9..436904a0 100644 --- a/src/components/private/OrgIdpTable.tsx +++ b/src/components/private/OrgIdpTable.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { ColumnDef } from "@tanstack/react-table"; diff --git a/src/components/private/RegionSelector.tsx b/src/components/private/RegionSelector.tsx index 56dde743..f3928345 100644 --- a/src/components/private/RegionSelector.tsx +++ b/src/components/private/RegionSelector.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useState } from "react"; diff --git a/src/components/private/SplashImage.tsx b/src/components/private/SplashImage.tsx index a2063692..c6ddc466 100644 --- a/src/components/private/SplashImage.tsx +++ b/src/components/private/SplashImage.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/components/private/ValidateSessionTransferToken.tsx b/src/components/private/ValidateSessionTransferToken.tsx index 116785d8..99ba42f1 100644 --- a/src/components/private/ValidateSessionTransferToken.tsx +++ b/src/components/private/ValidateSessionTransferToken.tsx @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; @@ -21,7 +8,7 @@ import { useEffect, useState } from "react"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; import { useTranslations } from "next-intl"; -import { TransferSessionResponse } from "@server/routers/auth/privateTransferSession"; +import { TransferSessionResponse } from "#private/routers/auth/transferSession"; type ValidateSessionTransferTokenParams = { token: string; diff --git a/src/contexts/privateRemoteExitNodeContext.ts b/src/contexts/privateRemoteExitNodeContext.ts deleted file mode 100644 index 65567b7c..00000000 --- a/src/contexts/privateRemoteExitNodeContext.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; -import { createContext } from "react"; - -type RemoteExitNodeContextType = { - remoteExitNode: GetRemoteExitNodeResponse; - updateRemoteExitNode: (updatedRemoteExitNode: Partial) => void; -}; - -const RemoteExitNodeContext = createContext(undefined); - -export default RemoteExitNodeContext; diff --git a/src/contexts/privateSubscriptionStatusContext.ts b/src/contexts/privateSubscriptionStatusContext.ts deleted file mode 100644 index aa99c21f..00000000 --- a/src/contexts/privateSubscriptionStatusContext.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; -import { createContext } from "react"; - -type SubscriptionStatusContextType = { - subscriptionStatus: GetOrgSubscriptionResponse | null; - updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; - isActive: () => boolean; - getTier: () => string | null; -}; - -const PrivateSubscriptionStatusContext = createContext< - SubscriptionStatusContextType | undefined ->(undefined); - -export default PrivateSubscriptionStatusContext; diff --git a/src/contexts/remoteExitNodeContext.ts b/src/contexts/remoteExitNodeContext.ts new file mode 100644 index 00000000..f5834e82 --- /dev/null +++ b/src/contexts/remoteExitNodeContext.ts @@ -0,0 +1,11 @@ +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; +import { createContext } from "react"; + +type RemoteExitNodeContextType = { + remoteExitNode: GetRemoteExitNodeResponse; + updateRemoteExitNode: (updatedRemoteExitNode: Partial) => void; +}; + +const RemoteExitNodeContext = createContext(undefined); + +export default RemoteExitNodeContext; diff --git a/src/contexts/subscriptionStatusContext.ts b/src/contexts/subscriptionStatusContext.ts new file mode 100644 index 00000000..267c58af --- /dev/null +++ b/src/contexts/subscriptionStatusContext.ts @@ -0,0 +1,15 @@ +import { GetOrgSubscriptionResponse } from "#private/routers/billing"; +import { createContext } from "react"; + +type SubscriptionStatusContextType = { + subscriptionStatus: GetOrgSubscriptionResponse | null; + updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; + isActive: () => boolean; + getTier: () => string | null; +}; + +const SubscriptionStatusContext = createContext< + SubscriptionStatusContextType | undefined +>(undefined); + +export default SubscriptionStatusContext; diff --git a/src/hooks/privateUseRemoteExitNodeContext.ts b/src/hooks/privateUseRemoteExitNodeContext.ts deleted file mode 100644 index 8decdb36..00000000 --- a/src/hooks/privateUseRemoteExitNodeContext.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -"use client"; - -import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; -import { build } from "@server/build"; -import { useContext } from "react"; - -export function useRemoteExitNodeContext() { - if (build == "oss") { - return null; - } - const context = useContext(RemoteExitNodeContext); - if (context === undefined) { - throw new Error("useRemoteExitNodeContext must be used within a RemoteExitNodeProvider"); - } - return context; -} diff --git a/src/hooks/privateUseSubscriptionStatusContext.ts b/src/hooks/privateUseSubscriptionStatusContext.ts deleted file mode 100644 index a6a53ac9..00000000 --- a/src/hooks/privateUseSubscriptionStatusContext.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; -import { build } from "@server/build"; -import { useContext } from "react"; - -export function usePrivateSubscriptionStatusContext() { - if (build == "oss") { - return null; - } - const context = useContext(PrivateSubscriptionStatusContext); - if (context === undefined) { - throw new Error( - "usePrivateSubscriptionStatusContext must be used within an PrivateSubscriptionStatusProvider" - ); - } - return context; -} diff --git a/src/hooks/privateUseCertificate.ts b/src/hooks/useCertificate.ts similarity index 88% rename from src/hooks/privateUseCertificate.ts rename to src/hooks/useCertificate.ts index 7956c3c7..b3c66080 100644 --- a/src/hooks/privateUseCertificate.ts +++ b/src/hooks/useCertificate.ts @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; import { useState, useCallback, useEffect } from "react"; import { AxiosResponse } from "axios"; -import { GetCertificateResponse } from "@server/routers/private/certificates"; +import { GetCertificateResponse } from "#private/routers/certificates"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/hooks/useRemoteExitNodeContext.ts b/src/hooks/useRemoteExitNodeContext.ts new file mode 100644 index 00000000..486147c4 --- /dev/null +++ b/src/hooks/useRemoteExitNodeContext.ts @@ -0,0 +1,16 @@ +"use client"; + +import RemoteExitNodeContext from "@app/contexts/remoteExitNodeContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function useRemoteExitNodeContext() { + if (build == "oss") { + return null; + } + const context = useContext(RemoteExitNodeContext); + if (context === undefined) { + throw new Error("useRemoteExitNodeContext must be used within a RemoteExitNodeProvider"); + } + return context; +} diff --git a/src/hooks/useSubscriptionStatusContext.ts b/src/hooks/useSubscriptionStatusContext.ts new file mode 100644 index 00000000..240168b1 --- /dev/null +++ b/src/hooks/useSubscriptionStatusContext.ts @@ -0,0 +1,16 @@ +import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; +import { build } from "@server/build"; +import { useContext } from "react"; + +export function useSubscriptionStatusContext() { + if (build == "oss") { + return null; + } + const context = useContext(SubscriptionStatusContext); + if (context === undefined) { + throw new Error( + "useSubscriptionStatusContext must be used within an SubscriptionStatusProvider" + ); + } + return context; +} diff --git a/src/lib/privateThemeColors.ts b/src/lib/themeColors.ts similarity index 87% rename from src/lib/privateThemeColors.ts rename to src/lib/themeColors.ts index 0543d68a..7c6bb46e 100644 --- a/src/lib/privateThemeColors.ts +++ b/src/lib/themeColors.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - const defaultTheme = { light: { background: "oklch(0.99 0 0)", diff --git a/src/providers/PrivateRemoteExitNodeProvider.tsx b/src/providers/RemoteExitNodeProvider.tsx similarity index 67% rename from src/providers/PrivateRemoteExitNodeProvider.tsx rename to src/providers/RemoteExitNodeProvider.tsx index 8dfd8f1a..72cfd107 100644 --- a/src/providers/PrivateRemoteExitNodeProvider.tsx +++ b/src/providers/RemoteExitNodeProvider.tsx @@ -1,20 +1,7 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import RemoteExitNodeContext from "@app/contexts/privateRemoteExitNodeContext"; -import { GetRemoteExitNodeResponse } from "@server/routers/private/remoteExitNode"; +import RemoteExitNodeContext from "@app/contexts/remoteExitNodeContext"; +import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; import { useState } from "react"; import { useTranslations } from "next-intl"; diff --git a/src/providers/PrivateSubscriptionStatusProvider.tsx b/src/providers/SubscriptionStatusProvider.tsx similarity index 72% rename from src/providers/PrivateSubscriptionStatusProvider.tsx rename to src/providers/SubscriptionStatusProvider.tsx index 9f4c5cd2..71a9401c 100644 --- a/src/providers/PrivateSubscriptionStatusProvider.tsx +++ b/src/providers/SubscriptionStatusProvider.tsx @@ -1,21 +1,8 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import PrivateSubscriptionStatusContext from "@app/contexts/privateSubscriptionStatusContext"; -import { getTierPriceSet } from "@server/lib/private/billing/tiers"; -import { GetOrgSubscriptionResponse } from "@server/routers/private/billing"; +import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; +import { getTierPriceSet } from "@server/lib/billing/tiers"; +import { GetOrgSubscriptionResponse } from "#private/routers/billing"; import { useState } from "react"; interface ProviderProps { @@ -68,7 +55,7 @@ export function PrivateSubscriptionStatusProvider({ }; return ( - {children} - + ); } diff --git a/src/providers/PrivateThemeDataProvider.tsx b/src/providers/ThemeDataProvider.tsx similarity index 67% rename from src/providers/PrivateThemeDataProvider.tsx rename to src/providers/ThemeDataProvider.tsx index a8da4551..821f9248 100644 --- a/src/providers/PrivateThemeDataProvider.tsx +++ b/src/providers/ThemeDataProvider.tsx @@ -1,19 +1,6 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - "use client"; -import setGlobalColorTheme from "@app/lib/privateThemeColors"; +import setGlobalColorTheme from "@app/lib/themeColors"; import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; diff --git a/tsconfig.json b/tsconfig.json index 7af98e53..e32eabd3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,11 @@ "@test/*": ["../test/*"], "@app/*": ["*"], "@cli/*": ["../cli/*"], - "@/*": ["./*"] + "@/*": ["./*"], + "#private/*": ["../server/private/*"], + "#open/*": ["../server/*"], + "#closed/*": ["../server/private/*"], + "#dynamic/*": ["../server/*"] }, "plugins": [ { From 6b125bba7c9315ee6cd1212d485be1e6515c5573 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 10 Oct 2025 11:52:45 -0700 Subject: [PATCH 211/322] reject user if no policies match and remove root user in auto provision --- server/routers/idp/validateOidcCallback.ts | 36 +++++++++++++++++-- .../(private)/idp/[idpId]/general/page.tsx | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 0ecf08b0..98bdfe44 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, Org } from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -32,6 +32,7 @@ import { decrypt } from "@server/lib/crypto"; import { UserType } from "@server/types/UserTypes"; import { FeatureId } from "@server/lib/billing"; import { usageService } from "@server/lib/billing/usageService"; +import { build } from "@server/build"; const ensureTrailingSlash = (url: string): string => { return url; @@ -255,7 +256,18 @@ export async function validateOidcCallback( ); if (existingIdp.idp.autoProvision) { - const allOrgs = await db.select().from(orgs); + let allOrgs: Org[] = []; + + if (build === "saas") { + const idpOrgs = await db + .select() + .from(idpOrg) + .where(eq(idpOrg.idpId, existingIdp.idp.idpId)) + .innerJoin(orgs, eq(orgs.orgId, idpOrg.orgId)); + allOrgs = idpOrgs.map((o) => o.orgs); + } else { + allOrgs = await db.select().from(orgs); + } const defaultRoleMapping = existingIdp.idp.defaultRoleMapping; const defaultOrgMapping = existingIdp.idp.defaultOrgMapping; @@ -292,6 +304,8 @@ export async function validateOidcCallback( } } + // user could be allowed in this org, now find the role + const roleMapping = idpOrgRes?.roleMapping || defaultRoleMapping; if (roleMapping) { @@ -336,6 +350,24 @@ export async function validateOidcCallback( let existingUserId = existingUser?.userId; + if (!userOrgInfo.length) { + if (existingUser) { + // delete the user + // cascade will also delete org users + + await db + .delete(users) + .where(eq(users.userId, existingUser.userId)); + } + + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + `No policies matched for ${userIdentifier}. This user must be added to an organization before logging in.` + ) + ); + } + const orgUserCounts: { orgId: string; userCount: number }[] = []; // sync the user with the orgs and roles diff --git a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx index a5be3c83..1d0a682f 100644 --- a/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx @@ -829,7 +829,7 @@ export default function GeneralPage() { {t( "idpJmespathAboutDescription" - )} + )}{" "} Date: Sat, 11 Oct 2025 19:01:33 -0700 Subject: [PATCH 212/322] Separate types & fix #private import --- server/db/pg/schema.ts | 3 +- server/db/sqlite/schema.ts | 3 +- server/lib/createUserAccountOrg.ts | 3 +- server/private/cleanup.ts | 13 +++++++ server/private/lib/rateLimit.ts | 2 +- .../private/routers/auth/transferSession.ts | 6 +--- .../routers/billing/getOrgSubscription.ts | 7 ++-- server/private/routers/billing/getOrgUsage.ts | 6 +--- .../routers/billing/internalGetOrgTier.ts | 6 +--- .../routers/certificates/getCertificate.ts | 15 +------- .../checkDomainNamespaceAvailability.ts | 10 +----- .../routers/loginPage/createLoginPage.ts | 3 +- .../routers/loginPage/deleteLoginPage.ts | 3 +- .../private/routers/loginPage/getLoginPage.ts | 5 +-- .../routers/loginPage/loadLoginPage.ts | 5 +-- .../routers/loginPage/updateLoginPage.ts | 3 +- .../routers/orgIdp/createOrgOidcIdp.ts | 6 +--- server/private/routers/orgIdp/getOrgIdp.ts | 5 +-- server/private/routers/orgIdp/listOrgIdps.ts | 10 +----- .../remoteExitNode/createRemoteExitNode.ts | 7 +--- .../remoteExitNode/getRemoteExitNode.ts | 3 +- .../remoteExitNode/getRemoteExitNodeToken.ts | 2 -- .../remoteExitNode/listRemoteExitNodes.ts | 8 ++--- .../pickRemoteExitNodeDefaults.ts | 6 +--- .../quickStartRemoteExitNode.ts | 8 ++--- server/routers/auth/types.ts | 4 +++ server/routers/billing/types.ts | 17 ++++++++++ server/routers/certificates/types.ts | 13 +++++++ server/routers/domain/types.ts | 8 +++++ server/routers/loginPage/types.ts | 11 ++++++ server/routers/orgIdp/types.ts | 27 +++++++++++++++ server/routers/remoteExitNode/types.ts | 34 +++++++++++++++++++ src/app/[orgId]/layout.tsx | 2 +- .../settings/(private)/billing/page.tsx | 2 +- .../[orgId]/settings/(private)/idp/page.tsx | 2 +- .../[remoteExitNodeId]/layout.tsx | 2 +- .../remote-exit-nodes/create/page.tsx | 2 +- .../(private)/remote-exit-nodes/page.tsx | 2 +- .../[orgId]/settings/sites/create/page.tsx | 2 +- src/app/auth/(private)/org/page.tsx | 7 ++-- .../auth/idp/[idpId]/oidc/callback/page.tsx | 2 +- src/app/auth/resource/[resourceGuid]/page.tsx | 6 ++-- src/components/DomainPicker.tsx | 2 +- src/components/private/AuthPageSettings.tsx | 2 +- .../private/ValidateSessionTransferToken.tsx | 2 +- src/contexts/remoteExitNodeContext.ts | 2 +- src/contexts/subscriptionStatusContext.ts | 2 +- src/hooks/useCertificate.ts | 2 +- src/providers/RemoteExitNodeProvider.tsx | 2 +- src/providers/SubscriptionStatusProvider.tsx | 2 +- 50 files changed, 177 insertions(+), 130 deletions(-) create mode 100644 server/routers/auth/types.ts create mode 100644 server/routers/billing/types.ts create mode 100644 server/routers/certificates/types.ts create mode 100644 server/routers/domain/types.ts create mode 100644 server/routers/loginPage/types.ts create mode 100644 server/routers/orgIdp/types.ts create mode 100644 server/routers/remoteExitNode/types.ts diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index e94bfe36..94e2475e 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -720,4 +720,5 @@ export type OrgDomains = InferSelectModel; export type SiteResource = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; -export type TargetHealthCheck = InferSelectModel; \ No newline at end of file +export type TargetHealthCheck = InferSelectModel; +export type IdpOidcConfig = InferSelectModel; \ No newline at end of file diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 9d64b85e..5ce415f2 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -759,4 +759,5 @@ export type SiteResource = InferSelectModel; export type OrgDomains = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; -export type TargetHealthCheck = InferSelectModel; \ No newline at end of file +export type TargetHealthCheck = InferSelectModel; +export type IdpOidcConfig = InferSelectModel; \ No newline at end of file diff --git a/server/lib/createUserAccountOrg.ts b/server/lib/createUserAccountOrg.ts index 8677d8e3..3728ec9b 100644 --- a/server/lib/createUserAccountOrg.ts +++ b/server/lib/createUserAccountOrg.ts @@ -15,8 +15,7 @@ import { } from "@server/db"; import { eq } from "drizzle-orm"; import { defaultRoleAllowedActions } from "@server/routers/role"; -import { FeatureId, limitsService, sandboxLimitSet } from "@server/lib/billing"; -import { createCustomer } from "@server/private/lib/billing/createCustomer"; +import { FeatureId, limitsService, sandboxLimitSet, createCustomer } from "@server/lib/billing"; import { usageService } from "@server/lib/billing/usageService"; export async function createUserAccountOrg( diff --git a/server/private/cleanup.ts b/server/private/cleanup.ts index 8e57ce29..8bf5ea3d 100644 --- a/server/private/cleanup.ts +++ b/server/private/cleanup.ts @@ -1,3 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + import { rateLimitService } from "#private/lib/rateLimit"; import { cleanup as wsCleanup } from "#private/routers/ws"; diff --git a/server/private/lib/rateLimit.ts b/server/private/lib/rateLimit.ts index 1d0e62ae..44aa0748 100644 --- a/server/private/lib/rateLimit.ts +++ b/server/private/lib/rateLimit.ts @@ -12,7 +12,7 @@ */ import logger from "@server/logger"; -import redisManager from "@server/private/lib/redis"; +import redisManager from "#private/lib/redis"; import { build } from "@server/build"; // Rate limiting configuration diff --git a/server/private/routers/auth/transferSession.ts b/server/private/routers/auth/transferSession.ts index e75f77dd..52138a75 100644 --- a/server/private/routers/auth/transferSession.ts +++ b/server/private/routers/auth/transferSession.ts @@ -26,6 +26,7 @@ import { sha256 } from "@oslojs/crypto/sha2"; import { serializeSessionCookie } from "@server/auth/sessions/app"; import { decrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; +import { TransferSessionResponse } from "@server/routers/auth/types"; const bodySchema = z.object({ token: z.string() @@ -33,11 +34,6 @@ const bodySchema = z.object({ export type TransferSessionBodySchema = z.infer; -export type TransferSessionResponse = { - valid: boolean; - cookie?: string; -}; - export async function transferSession( req: Request, res: Response, diff --git a/server/private/routers/billing/getOrgSubscription.ts b/server/private/routers/billing/getOrgSubscription.ts index 3e4f575e..b97ca39f 100644 --- a/server/private/routers/billing/getOrgSubscription.ts +++ b/server/private/routers/billing/getOrgSubscription.ts @@ -22,6 +22,8 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { GetOrgSubscriptionResponse } from "@server/routers/billing/types"; + // Import tables for billing import { customers, @@ -37,11 +39,6 @@ const getOrgSchema = z }) .strict(); -export type GetOrgSubscriptionResponse = { - subscription: Subscription | null; - items: SubscriptionItem[]; -}; - registry.registerPath({ method: "get", path: "/org/{orgId}/billing/subscription", diff --git a/server/private/routers/billing/getOrgUsage.ts b/server/private/routers/billing/getOrgUsage.ts index 5aad9609..bc879659 100644 --- a/server/private/routers/billing/getOrgUsage.ts +++ b/server/private/routers/billing/getOrgUsage.ts @@ -25,6 +25,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import { Limit, limits, Usage, usage } from "@server/db"; import { usageService } from "@server/lib/billing/usageService"; import { FeatureId } from "@server/lib/billing"; +import { GetOrgUsageResponse } from "@server/routers/billing/types"; const getOrgSchema = z .object({ @@ -32,11 +33,6 @@ const getOrgSchema = z }) .strict(); -export type GetOrgUsageResponse = { - usage: Usage[]; - limits: Limit[]; -}; - registry.registerPath({ method: "get", path: "/org/{orgId}/billing/usage", diff --git a/server/private/routers/billing/internalGetOrgTier.ts b/server/private/routers/billing/internalGetOrgTier.ts index 8db41807..cca96243 100644 --- a/server/private/routers/billing/internalGetOrgTier.ts +++ b/server/private/routers/billing/internalGetOrgTier.ts @@ -19,6 +19,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromZodError } from "zod-validation-error"; import { getOrgTierData } from "#private/lib/billing"; +import { GetOrgTierResponse } from "@server/routers/billing/types"; const getOrgSchema = z .object({ @@ -26,11 +27,6 @@ const getOrgSchema = z }) .strict(); -export type GetOrgTierResponse = { - tier: string | null; - active: boolean; -}; - export async function getOrgTier( req: Request, res: Response, diff --git a/server/private/routers/certificates/getCertificate.ts b/server/private/routers/certificates/getCertificate.ts index a0bf74f6..8392cbc0 100644 --- a/server/private/routers/certificates/getCertificate.ts +++ b/server/private/routers/certificates/getCertificate.ts @@ -21,6 +21,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { registry } from "@server/openApi"; +import { GetCertificateResponse } from "@server/routers/certificates/types"; const getCertificateSchema = z .object({ @@ -96,20 +97,6 @@ async function query(domainId: string, domain: string) { return existing.length > 0 ? existing[0] : null; } -export type GetCertificateResponse = { - certId: number; - domain: string; - domainId: string; - wildcard: boolean; - status: string; // pending, requested, valid, expired, failed - expiresAt: string | null; - lastRenewalAttempt: Date | null; - createdAt: string; - updatedAt: string; - errorMessage?: string | null; - renewalCount: number; -} - registry.registerPath({ method: "get", path: "/org/{orgId}/certificate/{domainId}/{domain}", diff --git a/server/private/routers/domain/checkDomainNamespaceAvailability.ts b/server/private/routers/domain/checkDomainNamespaceAvailability.ts index f1a4e103..745af9d3 100644 --- a/server/private/routers/domain/checkDomainNamespaceAvailability.ts +++ b/server/private/routers/domain/checkDomainNamespaceAvailability.ts @@ -21,6 +21,7 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { db, domainNamespaces, resources } from "@server/db"; import { inArray } from "drizzle-orm"; +import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types"; const paramsSchema = z.object({}).strict(); @@ -30,15 +31,6 @@ const querySchema = z }) .strict(); -export type CheckDomainAvailabilityResponse = { - available: boolean; - options: { - domainNamespaceId: string; - domainId: string; - fullDomain: string; - }[]; -}; - registry.registerPath({ method: "get", path: "/domain/check-namespace-availability", diff --git a/server/private/routers/loginPage/createLoginPage.ts b/server/private/routers/loginPage/createLoginPage.ts index 9c0359fe..cb0bb923 100644 --- a/server/private/routers/loginPage/createLoginPage.ts +++ b/server/private/routers/loginPage/createLoginPage.ts @@ -33,6 +33,7 @@ import { createCertificate } from "#private/routers/certificates/createCertifica import { getOrgTierData } from "#private/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; +import { CreateLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z .object({ @@ -49,8 +50,6 @@ const bodySchema = z export type CreateLoginPageBody = z.infer; -export type CreateLoginPageResponse = LoginPage; - export async function createLoginPage( req: Request, res: Response, diff --git a/server/private/routers/loginPage/deleteLoginPage.ts b/server/private/routers/loginPage/deleteLoginPage.ts index 7cc957a2..bf7941e7 100644 --- a/server/private/routers/loginPage/deleteLoginPage.ts +++ b/server/private/routers/loginPage/deleteLoginPage.ts @@ -20,6 +20,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq, and } from "drizzle-orm"; +import { DeleteLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z .object({ @@ -28,8 +29,6 @@ const paramsSchema = z }) .strict(); -export type DeleteLoginPageResponse = LoginPage; - export async function deleteLoginPage( req: Request, res: Response, diff --git a/server/private/routers/loginPage/getLoginPage.ts b/server/private/routers/loginPage/getLoginPage.ts index 9c9a18f5..76e20ffb 100644 --- a/server/private/routers/loginPage/getLoginPage.ts +++ b/server/private/routers/loginPage/getLoginPage.ts @@ -20,6 +20,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { GetLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z .object({ @@ -40,10 +41,6 @@ async function query(orgId: string) { return res?.loginPage; } -export type GetLoginPageResponse = NonNullable< - Awaited> ->; - export async function getLoginPage( req: Request, res: Response, diff --git a/server/private/routers/loginPage/loadLoginPage.ts b/server/private/routers/loginPage/loadLoginPage.ts index 91cc002e..133336b6 100644 --- a/server/private/routers/loginPage/loadLoginPage.ts +++ b/server/private/routers/loginPage/loadLoginPage.ts @@ -20,6 +20,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { LoadLoginPageResponse } from "@server/routers/loginPage/types"; const querySchema = z.object({ resourceId: z.coerce.number().int().positive().optional(), @@ -70,10 +71,6 @@ async function query(orgId: string | undefined, fullDomain: string) { }; } -export type LoadLoginPageResponse = NonNullable< - Awaited> -> & { orgId: string }; - export async function loadLoginPage( req: Request, res: Response, diff --git a/server/private/routers/loginPage/updateLoginPage.ts b/server/private/routers/loginPage/updateLoginPage.ts index 4aebbb7a..4f2be084 100644 --- a/server/private/routers/loginPage/updateLoginPage.ts +++ b/server/private/routers/loginPage/updateLoginPage.ts @@ -26,6 +26,7 @@ import { createCertificate } from "#private/routers/certificates/createCertifica import { getOrgTierData } from "#private/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; +import { UpdateLoginPageResponse } from "@server/routers/loginPage/types"; const paramsSchema = z .object({ @@ -55,8 +56,6 @@ const bodySchema = z export type UpdateLoginPageBody = z.infer; -export type UpdateLoginPageResponse = LoginPage; - export async function updateLoginPage( req: Request, res: Response, diff --git a/server/private/routers/orgIdp/createOrgOidcIdp.ts b/server/private/routers/orgIdp/createOrgOidcIdp.ts index 5f732864..02cef526 100644 --- a/server/private/routers/orgIdp/createOrgOidcIdp.ts +++ b/server/private/routers/orgIdp/createOrgOidcIdp.ts @@ -27,6 +27,7 @@ import config from "@server/lib/config"; import { build } from "@server/build"; import { getOrgTierData } from "#private/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; +import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types"; const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict(); @@ -47,11 +48,6 @@ const bodySchema = z }) .strict(); -export type CreateOrgIdpResponse = { - idpId: number; - redirectUrl: string; -}; - // registry.registerPath({ // method: "put", // path: "/idp/oidc", diff --git a/server/private/routers/orgIdp/getOrgIdp.ts b/server/private/routers/orgIdp/getOrgIdp.ts index 73ccdcbb..0e6689fc 100644 --- a/server/private/routers/orgIdp/getOrgIdp.ts +++ b/server/private/routers/orgIdp/getOrgIdp.ts @@ -25,6 +25,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import config from "@server/lib/config"; import { decrypt } from "@server/lib/crypto"; import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; +import { GetOrgIdpResponse } from "@server/routers/orgIdp/types"; const paramsSchema = z .object({ @@ -47,10 +48,6 @@ async function query(idpId: number, orgId: string) { return res; } -export type GetOrgIdpResponse = NonNullable< - Awaited> -> & { redirectUrl: string }; - // registry.registerPath({ // method: "get", // path: "/idp/{idpId}", diff --git a/server/private/routers/orgIdp/listOrgIdps.ts b/server/private/routers/orgIdp/listOrgIdps.ts index 208732de..0c69ff8d 100644 --- a/server/private/routers/orgIdp/listOrgIdps.ts +++ b/server/private/routers/orgIdp/listOrgIdps.ts @@ -22,6 +22,7 @@ import { eq, sql } from "drizzle-orm"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types"; const querySchema = z .object({ @@ -65,15 +66,6 @@ async function query(orgId: string, limit: number, offset: number) { return res; } -export type ListOrgIdpsResponse = { - idps: Awaited>; - pagination: { - total: number; - limit: number; - offset: number; - }; -}; - // registry.registerPath({ // method: "get", // path: "/idp", diff --git a/server/private/routers/remoteExitNode/createRemoteExitNode.ts b/server/private/routers/remoteExitNode/createRemoteExitNode.ts index 44fecb86..4c0fa38a 100644 --- a/server/private/routers/remoteExitNode/createRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/createRemoteExitNode.ts @@ -29,17 +29,12 @@ import { and, eq } from "drizzle-orm"; import { getNextAvailableSubnet } from "@server/lib/exitNodes"; import { usageService } from "@server/lib/billing/usageService"; import { FeatureId } from "@server/lib/billing"; +import { CreateRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; export const paramsSchema = z.object({ orgId: z.string() }); -export type CreateRemoteExitNodeResponse = { - token: string; - remoteExitNodeId: string; - secret: string; -}; - const bodySchema = z .object({ remoteExitNodeId: z.string().length(15), diff --git a/server/private/routers/remoteExitNode/getRemoteExitNode.ts b/server/private/routers/remoteExitNode/getRemoteExitNode.ts index 19c4f263..2ef3fb06 100644 --- a/server/private/routers/remoteExitNode/getRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNode.ts @@ -21,6 +21,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; const getRemoteExitNodeSchema = z .object({ @@ -52,8 +53,6 @@ async function query(remoteExitNodeId: string) { return remoteExitNode; } -export type GetRemoteExitNodeResponse = Awaited>; - export async function getRemoteExitNode( req: Request, res: Response, diff --git a/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts index 48b0110d..16ec4d5d 100644 --- a/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts +++ b/server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts @@ -35,8 +35,6 @@ export const remoteExitNodeGetTokenBodySchema = z.object({ token: z.string().optional() }); -export type RemoteExitNodeGetTokenBody = z.infer; - export async function getRemoteExitNodeToken( req: Request, res: Response, diff --git a/server/private/routers/remoteExitNode/listRemoteExitNodes.ts b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts index d6d2466e..1029b1e9 100644 --- a/server/private/routers/remoteExitNode/listRemoteExitNodes.ts +++ b/server/private/routers/remoteExitNode/listRemoteExitNodes.ts @@ -21,6 +21,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; +import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types"; const listRemoteExitNodesParamsSchema = z .object({ @@ -43,7 +44,7 @@ const listRemoteExitNodesSchema = z.object({ .pipe(z.number().int().nonnegative()) }); -function queryRemoteExitNodes(orgId: string) { +export function queryRemoteExitNodes(orgId: string) { return db .select({ remoteExitNodeId: remoteExitNodes.remoteExitNodeId, @@ -65,11 +66,6 @@ function queryRemoteExitNodes(orgId: string) { ); } -export type ListRemoteExitNodesResponse = { - remoteExitNodes: Awaited>; - pagination: { total: number; limit: number; offset: number }; -}; - export async function listRemoteExitNodes( req: Request, res: Response, diff --git a/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts index 684e616c..e5762f0d 100644 --- a/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts +++ b/server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts @@ -19,11 +19,7 @@ import logger from "@server/logger"; import { generateId } from "@server/auth/sessions/app"; import { fromError } from "zod-validation-error"; import { z } from "zod"; - -export type PickRemoteExitNodeDefaultsResponse = { - remoteExitNodeId: string; - secret: string; -}; +import { PickRemoteExitNodeDefaultsResponse } from "@server/routers/remoteExitNode/types"; const paramsSchema = z .object({ diff --git a/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts index 689580b9..4d368152 100644 --- a/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts @@ -12,7 +12,7 @@ */ import { NextFunction, Request, Response } from "express"; -import { db, exitNodes, exitNodeOrgs } from "@server/db"; +import { db } from "@server/db"; import HttpCode from "@server/types/HttpCode"; import { remoteExitNodes } from "@server/db"; import createHttpError from "http-errors"; @@ -24,11 +24,7 @@ import { hashPassword } from "@server/auth/password"; import logger from "@server/logger"; import z from "zod"; import { fromError } from "zod-validation-error"; - -export type QuickStartRemoteExitNodeResponse = { - remoteExitNodeId: string; - secret: string; -}; +import { QuickStartRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; const INSTALLER_KEY = "af4e4785-7e09-11f0-b93a-74563c4e2a7e"; diff --git a/server/routers/auth/types.ts b/server/routers/auth/types.ts new file mode 100644 index 00000000..4c6148fa --- /dev/null +++ b/server/routers/auth/types.ts @@ -0,0 +1,4 @@ +export type TransferSessionResponse = { + valid: boolean; + cookie?: string; +}; \ No newline at end of file diff --git a/server/routers/billing/types.ts b/server/routers/billing/types.ts new file mode 100644 index 00000000..2ec5a1b1 --- /dev/null +++ b/server/routers/billing/types.ts @@ -0,0 +1,17 @@ +import { Limit, Subscription, SubscriptionItem, Usage } from "@server/db"; + +export type GetOrgSubscriptionResponse = { + subscription: Subscription | null; + items: SubscriptionItem[]; +}; + +export type GetOrgUsageResponse = { + usage: Usage[]; + limits: Limit[]; +}; + +export type GetOrgTierResponse = { + tier: string | null; + active: boolean; +}; + diff --git a/server/routers/certificates/types.ts b/server/routers/certificates/types.ts new file mode 100644 index 00000000..80136de8 --- /dev/null +++ b/server/routers/certificates/types.ts @@ -0,0 +1,13 @@ +export type GetCertificateResponse = { + certId: number; + domain: string; + domainId: string; + wildcard: boolean; + status: string; // pending, requested, valid, expired, failed + expiresAt: string | null; + lastRenewalAttempt: Date | null; + createdAt: string; + updatedAt: string; + errorMessage?: string | null; + renewalCount: number; +} \ No newline at end of file diff --git a/server/routers/domain/types.ts b/server/routers/domain/types.ts new file mode 100644 index 00000000..4ae48fb1 --- /dev/null +++ b/server/routers/domain/types.ts @@ -0,0 +1,8 @@ +export type CheckDomainAvailabilityResponse = { + available: boolean; + options: { + domainNamespaceId: string; + domainId: string; + fullDomain: string; + }[]; +}; \ No newline at end of file diff --git a/server/routers/loginPage/types.ts b/server/routers/loginPage/types.ts new file mode 100644 index 00000000..26f59cab --- /dev/null +++ b/server/routers/loginPage/types.ts @@ -0,0 +1,11 @@ +import { LoginPage } from "@server/db"; + +export type CreateLoginPageResponse = LoginPage; + +export type DeleteLoginPageResponse = LoginPage; + +export type GetLoginPageResponse = LoginPage; + +export type UpdateLoginPageResponse = LoginPage; + +export type LoadLoginPageResponse = LoginPage & { orgId: string }; \ No newline at end of file diff --git a/server/routers/orgIdp/types.ts b/server/routers/orgIdp/types.ts new file mode 100644 index 00000000..a8e205cc --- /dev/null +++ b/server/routers/orgIdp/types.ts @@ -0,0 +1,27 @@ +import { Idp, IdpOidcConfig } from "@server/db"; + +export type CreateOrgIdpResponse = { + idpId: number; + redirectUrl: string; +}; + +export type GetOrgIdpResponse = { + idp: Idp, + idpOidcConfig: IdpOidcConfig | null, + redirectUrl: string +} + +export type ListOrgIdpsResponse = { + idps: { + idpId: number; + orgId: string; + name: string; + type: string; + variant: string; + }[], + pagination: { + total: number; + limit: number; + offset: number; + }; +}; diff --git a/server/routers/remoteExitNode/types.ts b/server/routers/remoteExitNode/types.ts new file mode 100644 index 00000000..55d0a286 --- /dev/null +++ b/server/routers/remoteExitNode/types.ts @@ -0,0 +1,34 @@ +import { RemoteExitNode } from "@server/db"; + +export type CreateRemoteExitNodeResponse = { + token: string; + remoteExitNodeId: string; + secret: string; +}; + +export type PickRemoteExitNodeDefaultsResponse = { + remoteExitNodeId: string; + secret: string; +}; + +export type QuickStartRemoteExitNodeResponse = { + remoteExitNodeId: string; + secret: string; +}; + +export type ListRemoteExitNodesResponse = { + remoteExitNodes: { + remoteExitNodeId: string; + dateCreated: string; + version: string | null; + exitNodeId: number | null; + name: string; + address: string; + endpoint: string; + online: boolean; + type: string | null; + }[]; + pagination: { total: number; limit: number; offset: number }; +}; + +export type GetRemoteExitNodeResponse = { remoteExitNodeId: string; dateCreated: string; version: string | null; exitNodeId: number | null; name: string; address: string; endpoint: string; online: boolean; type: string | null; } \ No newline at end of file diff --git a/src/app/[orgId]/layout.tsx b/src/app/[orgId]/layout.tsx index d8f28a59..b03f06e8 100644 --- a/src/app/[orgId]/layout.tsx +++ b/src/app/[orgId]/layout.tsx @@ -10,7 +10,7 @@ import { redirect } from "next/navigation"; import { cache } from "react"; import SetLastOrgCookie from "@app/components/SetLastOrgCookie"; import PrivateSubscriptionStatusProvider from "@app/providers/SubscriptionStatusProvider"; -import { GetOrgSubscriptionResponse } from "#private/routers/billing/getOrgSubscription"; +import { GetOrgSubscriptionResponse } from "@server/routers/billing/types"; import { pullEnv } from "@app/lib/pullEnv"; import { build } from "@server/build"; diff --git a/src/app/[orgId]/settings/(private)/billing/page.tsx b/src/app/[orgId]/settings/(private)/billing/page.tsx index cab22311..a2841fb4 100644 --- a/src/app/[orgId]/settings/(private)/billing/page.tsx +++ b/src/app/[orgId]/settings/(private)/billing/page.tsx @@ -37,7 +37,7 @@ import { InfoPopup } from "@/components/ui/info-popup"; import { GetOrgSubscriptionResponse, GetOrgUsageResponse -} from "#private/routers/billing"; +} from "@server/routers/billing/types"; import { useTranslations } from "use-intl"; import Link from "next/link"; diff --git a/src/app/[orgId]/settings/(private)/idp/page.tsx b/src/app/[orgId]/settings/(private)/idp/page.tsx index 2d1882b9..b27cf63b 100644 --- a/src/app/[orgId]/settings/(private)/idp/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/page.tsx @@ -9,7 +9,7 @@ import { cache } from "react"; import { GetOrgSubscriptionResponse, GetOrgTierResponse -} from "#private/routers/billing"; +} from "@server/routers/billing/types"; import { TierId } from "@server/lib/billing/tiers"; import { build } from "@server/build"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx index bfab3086..7a7b3611 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx @@ -1,5 +1,5 @@ import { internal } from "@app/lib/api"; -import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; +import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { authCookieHeader } from "@app/lib/api/cookies"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx index 7531ba6d..ca3e0cba 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx @@ -30,7 +30,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { QuickStartRemoteExitNodeResponse, PickRemoteExitNodeDefaultsResponse -} from "#private/routers/remoteExitNode"; +} from "@server/routers/remoteExitNode/types"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter, useSearchParams } from "next/navigation"; diff --git a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx index c4113558..632dc0ad 100644 --- a/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx +++ b/src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx @@ -1,6 +1,6 @@ import { internal } from "@app/lib/api"; import { authCookieHeader } from "@app/lib/api/cookies"; -import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types"; import { AxiosResponse } from "axios"; import ExitNodesTable, { RemoteExitNodeRow } from "./ExitNodesTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 73ead11f..c0b8c2a6 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -53,7 +53,7 @@ import { CreateSiteResponse, PickSiteDefaultsResponse } from "@server/routers/site"; -import { ListRemoteExitNodesResponse } from "#private/routers/remoteExitNode"; +import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types"; import { toast } from "@app/hooks/useToast"; import { AxiosResponse } from "axios"; import { useParams, useRouter } from "next/navigation"; diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index c438ba66..70821f8f 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -6,13 +6,12 @@ import { verifySession } from "@app/lib/auth/verifySession"; import { redirect } from "next/navigation"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; -import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; +import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types"; import { build } from "@server/build"; import { headers } from "next/headers"; import { - GetLoginPageResponse, LoadLoginPageResponse -} from "#private/routers/loginPage"; +} from "@server/routers/loginPage/types"; import IdpLoginButtons from "@app/components/private/IdpLoginButtons"; import { Card, @@ -26,7 +25,7 @@ import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { GetSessionTransferTokenRenponse } from "#private/routers/auth/getSessionTransferToken"; import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken"; -import { GetOrgTierResponse } from "#private/routers/billing"; +import { GetOrgTierResponse } from "@server/routers/billing/types"; import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx index 6c503da8..a2432e3e 100644 --- a/src/app/auth/idp/[idpId]/oidc/callback/page.tsx +++ b/src/app/auth/idp/[idpId]/oidc/callback/page.tsx @@ -6,7 +6,7 @@ import { AxiosResponse } from "axios"; import { GetIdpResponse } from "@server/routers/idp"; import { getTranslations } from "next-intl/server"; import { pullEnv } from "@app/lib/pullEnv"; -import { LoadLoginPageResponse } from "#private/routers/loginPage"; +import { LoadLoginPageResponse } from "@server/routers/loginPage/types"; import { redirect } from "next/navigation"; export const dynamic = "force-dynamic"; diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index d37bc8ca..c7aec861 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -15,12 +15,12 @@ import AccessToken from "@app/components/AccessToken"; import { pullEnv } from "@app/lib/pullEnv"; import { LoginFormIDP } from "@app/components/LoginForm"; import { ListIdpsResponse } from "@server/routers/idp"; -import { ListOrgIdpsResponse } from "#private/routers/orgIdp"; +import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types"; import AutoLoginHandler from "@app/components/AutoLoginHandler"; import { build } from "@server/build"; import { headers } from "next/headers"; -import { GetLoginPageResponse } from "#private/routers/loginPage"; -import { GetOrgTierResponse } from "#private/routers/billing"; +import { GetLoginPageResponse } from "@server/routers/loginPage/types"; +import { GetOrgTierResponse } from "@server/routers/billing/types"; import { TierId } from "@server/lib/billing/tiers"; export const dynamic = "force-dynamic"; diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 20c9960c..78273ddf 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -32,7 +32,7 @@ import { createApiClient, formatAxiosError } from "@/lib/api"; import { useEnvContext } from "@/hooks/useEnvContext"; import { toast } from "@/hooks/useToast"; import { ListDomainsResponse } from "@server/routers/domain/listDomains"; -import { CheckDomainAvailabilityResponse } from "#private/routers/domain/checkDomainNamespaceAvailability"; +import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types"; import { AxiosResponse } from "axios"; import { cn } from "@/lib/cn"; import { useTranslations } from "next-intl"; diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 46c5ede0..8da85468 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -30,7 +30,7 @@ import { SettingsSectionForm } from "@app/components/Settings"; import { useTranslations } from "next-intl"; -import { GetLoginPageResponse } from "#private/routers/loginPage"; +import { GetLoginPageResponse } from "@server/routers/loginPage/types"; import { ListDomainsResponse } from "@server/routers/domain"; import { DomainRow } from "@app/components/DomainsTable"; import { toUnicode } from "punycode"; diff --git a/src/components/private/ValidateSessionTransferToken.tsx b/src/components/private/ValidateSessionTransferToken.tsx index 99ba42f1..fcb6a026 100644 --- a/src/components/private/ValidateSessionTransferToken.tsx +++ b/src/components/private/ValidateSessionTransferToken.tsx @@ -8,7 +8,7 @@ import { useEffect, useState } from "react"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; import { useTranslations } from "next-intl"; -import { TransferSessionResponse } from "#private/routers/auth/transferSession"; +import { TransferSessionResponse } from "@server/routers/auth/types"; type ValidateSessionTransferTokenParams = { token: string; diff --git a/src/contexts/remoteExitNodeContext.ts b/src/contexts/remoteExitNodeContext.ts index f5834e82..8f556480 100644 --- a/src/contexts/remoteExitNodeContext.ts +++ b/src/contexts/remoteExitNodeContext.ts @@ -1,4 +1,4 @@ -import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; +import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; import { createContext } from "react"; type RemoteExitNodeContextType = { diff --git a/src/contexts/subscriptionStatusContext.ts b/src/contexts/subscriptionStatusContext.ts index 267c58af..33f05479 100644 --- a/src/contexts/subscriptionStatusContext.ts +++ b/src/contexts/subscriptionStatusContext.ts @@ -1,4 +1,4 @@ -import { GetOrgSubscriptionResponse } from "#private/routers/billing"; +import { GetOrgSubscriptionResponse } from "@server/routers/billing/types"; import { createContext } from "react"; type SubscriptionStatusContextType = { diff --git a/src/hooks/useCertificate.ts b/src/hooks/useCertificate.ts index b3c66080..bdb09e08 100644 --- a/src/hooks/useCertificate.ts +++ b/src/hooks/useCertificate.ts @@ -2,7 +2,7 @@ import { useState, useCallback, useEffect } from "react"; import { AxiosResponse } from "axios"; -import { GetCertificateResponse } from "#private/routers/certificates"; +import { GetCertificateResponse } from "@server/routers/certificates/types"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/providers/RemoteExitNodeProvider.tsx b/src/providers/RemoteExitNodeProvider.tsx index 72cfd107..a1bceaff 100644 --- a/src/providers/RemoteExitNodeProvider.tsx +++ b/src/providers/RemoteExitNodeProvider.tsx @@ -1,7 +1,7 @@ "use client"; import RemoteExitNodeContext from "@app/contexts/remoteExitNodeContext"; -import { GetRemoteExitNodeResponse } from "#private/routers/remoteExitNode"; +import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types"; import { useState } from "react"; import { useTranslations } from "next-intl"; diff --git a/src/providers/SubscriptionStatusProvider.tsx b/src/providers/SubscriptionStatusProvider.tsx index 71a9401c..78c3b22a 100644 --- a/src/providers/SubscriptionStatusProvider.tsx +++ b/src/providers/SubscriptionStatusProvider.tsx @@ -2,7 +2,7 @@ import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; import { getTierPriceSet } from "@server/lib/billing/tiers"; -import { GetOrgSubscriptionResponse } from "#private/routers/billing"; +import { GetOrgSubscriptionResponse } from "@server/routers/billing/types"; import { useState } from "react"; interface ProviderProps { From f17a95705814199e8f1f17d60d57c124d9a8fdf6 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 11 Oct 2025 20:46:49 -0700 Subject: [PATCH 213/322] Cleaning up more imports --- server/routers/auth/types.ts | 4 ++++ server/routers/supporterKey/isSupporterKeyVisible.ts | 1 - src/app/auth/(private)/org/page.tsx | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/routers/auth/types.ts b/server/routers/auth/types.ts index 4c6148fa..bb5a1b4e 100644 --- a/server/routers/auth/types.ts +++ b/server/routers/auth/types.ts @@ -1,4 +1,8 @@ export type TransferSessionResponse = { valid: boolean; cookie?: string; +}; + +export type GetSessionTransferTokenRenponse = { + token: string; }; \ No newline at end of file diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index 317f6461..8dda516e 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -3,7 +3,6 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import privateConfig from "#private/lib/config"; import config from "@server/lib/config"; import { db } from "@server/db"; import { count } from "drizzle-orm"; diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index 70821f8f..5b675b00 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -23,7 +23,7 @@ import { import { Button } from "@app/components/ui/button"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; -import { GetSessionTransferTokenRenponse } from "#private/routers/auth/getSessionTransferToken"; +import { GetSessionTransferTokenRenponse } from "@server/routers/auth/types"; import ValidateSessionTransferToken from "@app/components/private/ValidateSessionTransferToken"; import { GetOrgTierResponse } from "@server/routers/billing/types"; import { TierId } from "@server/lib/billing/tiers"; From a50c0d84e9ab21107b52dd30b6e12f3f393bbcd0 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 12 Oct 2025 16:23:38 -0700 Subject: [PATCH 214/322] Make easier to run in dev - fix a couple of things --- drizzle.pg.config.ts | 2 +- drizzle.sqlite.config.ts | 2 +- messages/en-US.json | 6 +- server/apiServer.ts | 34 ++- server/lib/billing/usageService.ts | 206 ++++++++++++------ server/private/lib/stripe.ts | 4 +- server/private/routers/external.ts | 5 +- server/private/routers/hybrid.ts | 86 +++++++- .../remoteExitNode/createRemoteExitNode.ts | 41 ++-- tsconfig.json | 2 +- 10 files changed, 265 insertions(+), 123 deletions(-) diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index 8fc99161..9463d980 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -4,7 +4,7 @@ import { build } from "@server/build"; const schema = [ path.join("server", "db", "pg", "schema.ts"), - path.join("server", "db", "pg", "pSchema.ts") + path.join("server", "db", "pg", "privateSchema.ts") ]; export default defineConfig({ diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index b8679aa9..6209b21e 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -5,7 +5,7 @@ import path from "path"; const schema = [ path.join("server", "db", "sqlite", "schema.ts"), - path.join("server", "db", "sqlite", "pSchema.ts") + path.join("server", "db", "sqlite", "privateSchema.ts") ]; export default defineConfig({ diff --git a/messages/en-US.json b/messages/en-US.json index 9435b5d7..f725f853 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1542,8 +1542,8 @@ "autoLoginError": "Auto Login Error", "autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.", "autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.", - "remoteExitNodeManageRemoteExitNodes": "Managed Nodes", - "remoteExitNodeDescription": "Self-host one or more nodes for tunnel exit servers", + "remoteExitNodeManageRemoteExitNodes": "Remote Nodes", + "remoteExitNodeDescription": "Self-host one or more remote nodes for tunnel exit servers", "remoteExitNodes": "Nodes", "searchRemoteExitNodes": "Search nodes...", "remoteExitNodeAdd": "Add Node", @@ -1553,7 +1553,7 @@ "remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.", "remoteExitNodeConfirmDelete": "Confirm Delete Node", "remoteExitNodeDelete": "Delete Node", - "sidebarRemoteExitNodes": "Nodes", + "sidebarRemoteExitNodes": "Remote Nodes", "remoteExitNodeCreate": { "title": "Create Node", "description": "Create a new node to extend your network connectivity", diff --git a/server/apiServer.ts b/server/apiServer.ts index 9a626769..6c490053 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -44,27 +44,25 @@ export function createApiServer() { } const corsConfig = config.getRawConfig().server.cors; + const options = { + ...(corsConfig?.origins + ? { origin: corsConfig.origins } + : { + origin: (origin: any, callback: any) => { + callback(null, true); + } + }), + ...(corsConfig?.methods && { methods: corsConfig.methods }), + ...(corsConfig?.allowed_headers && { + allowedHeaders: corsConfig.allowed_headers + }), + credentials: !(corsConfig?.credentials === false) + }; - if (build == "oss") { - const options = { - ...(corsConfig?.origins - ? { origin: corsConfig.origins } - : { - origin: (origin: any, callback: any) => { - callback(null, true); - } - }), - ...(corsConfig?.methods && { methods: corsConfig.methods }), - ...(corsConfig?.allowed_headers && { - allowedHeaders: corsConfig.allowed_headers - }), - credentials: !(corsConfig?.credentials === false) - }; - + if (build == "oss" || !corsConfig) { logger.debug("Using CORS options", options); - apiServer.use(cors(options)); - } else { + } else if (corsConfig) { // Use the custom CORS middleware with loginPage support apiServer.use(corsWithLoginPageSupport(corsConfig)); } diff --git a/server/lib/billing/usageService.ts b/server/lib/billing/usageService.ts index edff41f0..0b2b095f 100644 --- a/server/lib/billing/usageService.ts +++ b/server/lib/billing/usageService.ts @@ -31,6 +31,17 @@ interface StripeEvent { }; } +export function noop() { + if ( + build !== "saas" || + !process.env.S3_BUCKET || + !process.env.LOCAL_FILE_PATH + ) { + return true; + } + return false; +} + export class UsageService { private cache: NodeCache; private bucketName: string | undefined; @@ -41,7 +52,7 @@ export class UsageService { constructor() { this.cache = new NodeCache({ stdTTL: 300 }); // 5 minute TTL - if (build !== "saas") { + if (noop()) { return; } // this.bucketName = privateConfig.getRawPrivateConfig().stripe?.s3Bucket; @@ -71,7 +82,9 @@ export class UsageService { private async initializeEventsDirectory(): Promise { if (!this.eventsDir) { - logger.warn("Stripe local file path is not configured, skipping events directory initialization."); + logger.warn( + "Stripe local file path is not configured, skipping events directory initialization." + ); return; } try { @@ -83,7 +96,9 @@ export class UsageService { private async uploadPendingEventFilesOnStartup(): Promise { if (!this.eventsDir || !this.bucketName) { - logger.warn("Stripe local file path or bucket name is not configured, skipping leftover event file upload."); + logger.warn( + "Stripe local file path or bucket name is not configured, skipping leftover event file upload." + ); return; } try { @@ -106,15 +121,17 @@ export class UsageService { ContentType: "application/json" }); await s3Client.send(uploadCommand); - + // Check if file still exists before unlinking try { await fs.access(filePath); await fs.unlink(filePath); } catch (unlinkError) { - logger.debug(`Startup file ${file} was already deleted`); + logger.debug( + `Startup file ${file} was already deleted` + ); } - + logger.info( `Uploaded leftover event file ${file} to S3 with ${events.length} events` ); @@ -124,7 +141,9 @@ export class UsageService { await fs.access(filePath); await fs.unlink(filePath); } catch (unlinkError) { - logger.debug(`Empty startup file ${file} was already deleted`); + logger.debug( + `Empty startup file ${file} was already deleted` + ); } } } catch (err) { @@ -135,8 +154,8 @@ export class UsageService { } } } - } catch (err) { - logger.error("Failed to scan for leftover event files:", err); + } catch (error) { + logger.error("Failed to scan for leftover event files"); } } @@ -146,17 +165,17 @@ export class UsageService { value: number, transaction: any = null ): Promise { - if (build !== "saas") { + if (noop()) { return null; } - + // Truncate value to 11 decimal places value = this.truncateValue(value); - + // Implement retry logic for deadlock handling const maxRetries = 3; let attempt = 0; - + while (attempt <= maxRetries) { try { // Get subscription data for this org (with caching) @@ -179,7 +198,12 @@ export class UsageService { ); } else { await db.transaction(async (trx) => { - usage = await this.internalAddUsage(orgId, featureId, value, trx); + usage = await this.internalAddUsage( + orgId, + featureId, + value, + trx + ); }); } @@ -189,25 +213,26 @@ export class UsageService { return usage || null; } catch (error: any) { // Check if this is a deadlock error - const isDeadlock = error?.code === '40P01' || - error?.cause?.code === '40P01' || - (error?.message && error.message.includes('deadlock')); - + const isDeadlock = + error?.code === "40P01" || + error?.cause?.code === "40P01" || + (error?.message && error.message.includes("deadlock")); + if (isDeadlock && attempt < maxRetries) { attempt++; // Exponential backoff with jitter: 50-150ms, 100-300ms, 200-600ms const baseDelay = Math.pow(2, attempt - 1) * 50; const jitter = Math.random() * baseDelay; const delay = baseDelay + jitter; - + logger.warn( `Deadlock detected for ${orgId}/${featureId}, retrying attempt ${attempt}/${maxRetries} after ${delay.toFixed(0)}ms` ); - - await new Promise(resolve => setTimeout(resolve, delay)); + + await new Promise((resolve) => setTimeout(resolve, delay)); continue; } - + logger.error( `Failed to add usage for ${orgId}/${featureId} after ${attempt} attempts:`, error @@ -227,10 +252,10 @@ export class UsageService { ): Promise { // Truncate value to 11 decimal places value = this.truncateValue(value); - + const usageId = `${orgId}-${featureId}`; const meterId = getFeatureMeterId(featureId); - + // Use upsert: insert if not exists, otherwise increment const [returnUsage] = await trx .insert(usage) @@ -247,7 +272,8 @@ export class UsageService { set: { latestValue: sql`${usage.latestValue} + ${value}` } - }).returning(); + }) + .returning(); return returnUsage; } @@ -268,7 +294,7 @@ export class UsageService { value?: number, customerId?: string ): Promise { - if (build !== "saas") { + if (noop()) { return; } try { @@ -339,7 +365,7 @@ export class UsageService { .set({ latestValue: newRunningTotal, instantaneousValue: value, - updatedAt: Math.floor(Date.now() / 1000) + updatedAt: Math.floor(Date.now() / 1000) }) .where(eq(usage.usageId, usageId)); } @@ -354,7 +380,7 @@ export class UsageService { meterId, instantaneousValue: truncatedValue, latestValue: truncatedValue, - updatedAt: Math.floor(Date.now() / 1000) + updatedAt: Math.floor(Date.now() / 1000) }); } }); @@ -415,7 +441,7 @@ export class UsageService { ): Promise { // Truncate value to 11 decimal places before sending to Stripe const truncatedValue = this.truncateValue(value); - + const event: StripeEvent = { identifier: uuidv4(), timestamp: Math.floor(new Date().getTime() / 1000), @@ -432,7 +458,9 @@ export class UsageService { private async writeEventToFile(event: StripeEvent): Promise { if (!this.eventsDir || !this.bucketName) { - logger.warn("Stripe local file path or bucket name is not configured, skipping event file write."); + logger.warn( + "Stripe local file path or bucket name is not configured, skipping event file write." + ); return; } if (!this.currentEventFile) { @@ -481,7 +509,9 @@ export class UsageService { private async uploadFileToS3(): Promise { if (!this.bucketName || !this.eventsDir) { - logger.warn("Stripe local file path or bucket name is not configured, skipping S3 upload."); + logger.warn( + "Stripe local file path or bucket name is not configured, skipping S3 upload." + ); return; } if (!this.currentEventFile) { @@ -493,7 +523,9 @@ export class UsageService { // Check if this file is already being uploaded if (this.uploadingFiles.has(fileName)) { - logger.debug(`File ${fileName} is already being uploaded, skipping`); + logger.debug( + `File ${fileName} is already being uploaded, skipping` + ); return; } @@ -505,7 +537,9 @@ export class UsageService { try { await fs.access(filePath); } catch (error) { - logger.debug(`File ${fileName} does not exist, may have been already processed`); + logger.debug( + `File ${fileName} does not exist, may have been already processed` + ); this.uploadingFiles.delete(fileName); // Reset current file if it was this file if (this.currentEventFile === fileName) { @@ -525,7 +559,9 @@ export class UsageService { await fs.unlink(filePath); } catch (unlinkError) { // File may have been already deleted - logger.debug(`File ${fileName} was already deleted during cleanup`); + logger.debug( + `File ${fileName} was already deleted during cleanup` + ); } this.currentEventFile = null; this.uploadingFiles.delete(fileName); @@ -548,7 +584,9 @@ export class UsageService { await fs.unlink(filePath); } catch (unlinkError) { // File may have been already deleted by another process - logger.debug(`File ${fileName} was already deleted during upload`); + logger.debug( + `File ${fileName} was already deleted during upload` + ); } logger.info( @@ -559,10 +597,7 @@ export class UsageService { this.currentEventFile = null; this.currentFileStartTime = 0; } catch (error) { - logger.error( - `Failed to upload ${fileName} to S3:`, - error - ); + logger.error(`Failed to upload ${fileName} to S3:`, error); } finally { // Always remove from uploading set this.uploadingFiles.delete(fileName); @@ -579,7 +614,7 @@ export class UsageService { orgId: string, featureId: FeatureId ): Promise { - if (build !== "saas") { + if (noop()) { return null; } @@ -598,7 +633,7 @@ export class UsageService { `Creating new usage record for ${orgId}/${featureId}` ); const meterId = getFeatureMeterId(featureId); - + try { const [newUsage] = await db .insert(usage) @@ -653,7 +688,7 @@ export class UsageService { orgId: string, featureId: FeatureId ): Promise { - if (build !== "saas") { + if (noop()) { return null; } await this.updateDaily(orgId, featureId); // Ensure daily usage is updated @@ -673,7 +708,9 @@ export class UsageService { */ private async uploadOldEventFiles(): Promise { if (!this.eventsDir || !this.bucketName) { - logger.warn("Stripe local file path or bucket name is not configured, skipping old event file upload."); + logger.warn( + "Stripe local file path or bucket name is not configured, skipping old event file upload." + ); return; } try { @@ -681,15 +718,17 @@ export class UsageService { const now = Date.now(); for (const file of files) { if (!file.endsWith(".json")) continue; - + // Skip files that are already being uploaded if (this.uploadingFiles.has(file)) { - logger.debug(`Skipping file ${file} as it's already being uploaded`); + logger.debug( + `Skipping file ${file} as it's already being uploaded` + ); continue; } const filePath = path.join(this.eventsDir, file); - + try { // Check if file still exists before processing try { @@ -704,7 +743,7 @@ export class UsageService { if (age >= 90000) { // 1.5 minutes - Mark as being uploaded this.uploadingFiles.add(file); - + try { const fileContent = await fs.readFile( filePath, @@ -720,15 +759,17 @@ export class UsageService { ContentType: "application/json" }); await s3Client.send(uploadCommand); - + // Check if file still exists before unlinking try { await fs.access(filePath); await fs.unlink(filePath); } catch (unlinkError) { - logger.debug(`File ${file} was already deleted during interval upload`); + logger.debug( + `File ${file} was already deleted during interval upload` + ); } - + logger.info( `Interval: Uploaded event file ${file} to S3 with ${events.length} events` ); @@ -743,7 +784,9 @@ export class UsageService { await fs.access(filePath); await fs.unlink(filePath); } catch (unlinkError) { - logger.debug(`Empty file ${file} was already deleted`); + logger.debug( + `Empty file ${file} was already deleted` + ); } } } finally { @@ -765,12 +808,17 @@ export class UsageService { } } - public async checkLimitSet(orgId: string, kickSites = false, featureId?: FeatureId, usage?: Usage): Promise { - if (build !== "saas") { + public async checkLimitSet( + orgId: string, + kickSites = false, + featureId?: FeatureId, + usage?: Usage + ): Promise { + if (noop()) { return false; } // This method should check the current usage against the limits set for the organization - // and kick out all of the sites on the org + // and kick out all of the sites on the org let hasExceededLimits = false; try { @@ -805,16 +853,30 @@ export class UsageService { if (usage) { currentUsage = usage; } else { - currentUsage = await this.getUsage(orgId, limit.featureId as FeatureId); + currentUsage = await this.getUsage( + orgId, + limit.featureId as FeatureId + ); } - const usageValue = currentUsage?.instantaneousValue || currentUsage?.latestValue || 0; - logger.debug(`Current usage for org ${orgId} on feature ${limit.featureId}: ${usageValue}`); - logger.debug(`Limit for org ${orgId} on feature ${limit.featureId}: ${limit.value}`); - if (currentUsage && limit.value !== null && usageValue > limit.value) { + const usageValue = + currentUsage?.instantaneousValue || + currentUsage?.latestValue || + 0; + logger.debug( + `Current usage for org ${orgId} on feature ${limit.featureId}: ${usageValue}` + ); + logger.debug( + `Limit for org ${orgId} on feature ${limit.featureId}: ${limit.value}` + ); + if ( + currentUsage && + limit.value !== null && + usageValue > limit.value + ) { logger.debug( `Org ${orgId} has exceeded limit for ${limit.featureId}: ` + - `${usageValue} > ${limit.value}` + `${usageValue} > ${limit.value}` ); hasExceededLimits = true; break; // Exit early if any limit is exceeded @@ -823,7 +885,9 @@ export class UsageService { // If any limits are exceeded, disconnect all sites for this organization if (hasExceededLimits && kickSites) { - logger.warn(`Disconnecting all sites for org ${orgId} due to exceeded limits`); + logger.warn( + `Disconnecting all sites for org ${orgId} due to exceeded limits` + ); // Get all sites for this organization const orgSites = await db @@ -832,7 +896,7 @@ export class UsageService { .where(eq(sites.orgId, orgId)); // Mark all sites as offline and send termination messages - const siteUpdates = orgSites.map(site => site.siteId); + const siteUpdates = orgSites.map((site) => site.siteId); if (siteUpdates.length > 0) { // Send termination messages to newt sites @@ -853,17 +917,21 @@ export class UsageService { }; // Don't await to prevent blocking - sendToClient(newt.newtId, payload).catch((error: any) => { - logger.error( - `Failed to send termination message to newt ${newt.newtId}:`, - error - ); - }); + sendToClient(newt.newtId, payload).catch( + (error: any) => { + logger.error( + `Failed to send termination message to newt ${newt.newtId}:`, + error + ); + } + ); } } } - logger.info(`Disconnected ${orgSites.length} sites for org ${orgId} due to exceeded limits`); + logger.info( + `Disconnected ${orgSites.length} sites for org ${orgId} due to exceeded limits` + ); } } } catch (error) { diff --git a/server/private/lib/stripe.ts b/server/private/lib/stripe.ts index 477934b4..01aacb35 100644 --- a/server/private/lib/stripe.ts +++ b/server/private/lib/stripe.ts @@ -14,10 +14,10 @@ import Stripe from "stripe"; import privateConfig from "#private/lib/config"; import logger from "@server/logger"; -import { build } from "@server/build"; +import { noop } from "@server/lib/billing/usageService"; let stripe: Stripe | undefined = undefined; -if (build == "saas") { +if (!noop()) { const stripeApiKey = privateConfig.getRawPrivateConfig().stripe?.secret_key; if (!stripeApiKey) { logger.error("Stripe secret key is not configured"); diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index fac7c0c4..383fb6b2 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -33,10 +33,11 @@ import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { unauthenticated as ua, authenticated as a } from "@server/routers/external"; +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; unauthenticated.post( "/quick-start", @@ -227,8 +228,6 @@ authenticated.get( loginPage.getLoginPage ); -export const authRouter = Router(); - authRouter.post( "/remoteExitNode/get-token", rateLimit({ diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index 6d817853..0c1c543c 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -68,10 +68,11 @@ import { decryptData } from "@server/lib/encryption"; import config from "@server/lib/config"; import privateConfig from "#private/lib/config"; import * as fs from "fs"; -import { exchangeSession } from "@server/routers/badger"; +import { exchangeSession } from "@server/routers/badger"; import { validateResourceSessionToken } from "@server/auth/sessions/resource"; import { checkExitNodeOrg, resolveExitNodes } from "#private/lib/exitNodes"; import { maxmindLookup } from "@server/db/maxmind"; +import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; // Zod schemas for request validation const getResourceByDomainParamsSchema = z @@ -162,6 +163,14 @@ const validateResourceSessionTokenBodySchema = z }) .strict(); +const validateResourceAccessTokenBodySchema = z + .object({ + accessTokenId: z.string().optional(), + resourceId: z.number().optional(), + accessToken: z.string() + }) + .strict(); + // Certificates by domains query validation const getCertificatesByDomainsQuerySchema = z .object({ @@ -215,6 +224,33 @@ export type UserSessionWithUser = { export const hybridRouter = Router(); hybridRouter.use(verifySessionRemoteExitNodeMiddleware); +hybridRouter.get( + "/general-config", + async (req: Request, res: Response, next: NextFunction) => { + return response(res, { + data: { + resource_session_request_param: + config.getRawConfig().server.resource_session_request_param, + resource_access_token_headers: + config.getRawConfig().server.resource_access_token_headers, + resource_access_token_param: + config.getRawConfig().server.resource_access_token_param, + session_cookie_name: + config.getRawConfig().server.session_cookie_name, + require_email_verification: + config.getRawConfig().flags?.require_email_verification || + false, + resource_session_length_hours: + config.getRawConfig().server.resource_session_length_hours + }, + success: true, + error: false, + message: "General config retrieved successfully", + status: HttpCode.OK + }); + } +); + hybridRouter.get( "/traefik-config", async (req: Request, res: Response, next: NextFunction) => { @@ -1101,6 +1137,52 @@ hybridRouter.post( } ); +// Validate resource session token +hybridRouter.post( + "/resource/:resourceId/access-token/verify", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedBody = validateResourceAccessTokenBodySchema.safeParse( + req.body + ); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { accessToken, resourceId, accessTokenId } = parsedBody.data; + + const result = await verifyResourceAccessToken({ + accessTokenId, + accessToken, + resourceId + }); + + return response(res, { + data: result, + success: true, + error: false, + message: result.valid + ? "Resource access token is valid" + : "Resource access token is invalid or expired", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to validate resource session token" + ) + ); + } + } +); + const geoIpLookupParamsSchema = z.object({ ip: z.string().ip() }); @@ -1489,4 +1571,4 @@ hybridRouter.post( ); } } -); \ No newline at end of file +); diff --git a/server/private/routers/remoteExitNode/createRemoteExitNode.ts b/server/private/routers/remoteExitNode/createRemoteExitNode.ts index 4c0fa38a..28102fab 100644 --- a/server/private/routers/remoteExitNode/createRemoteExitNode.ts +++ b/server/private/routers/remoteExitNode/createRemoteExitNode.ts @@ -84,30 +84,25 @@ export async function createRemoteExitNode( orgId, FeatureId.REMOTE_EXIT_NODES ); - if (!usage) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - "No usage data found for this organization" - ) - ); - } - const rejectRemoteExitNodes = await usageService.checkLimitSet( - orgId, - false, - FeatureId.REMOTE_EXIT_NODES, - { - ...usage, - instantaneousValue: (usage.instantaneousValue || 0) + 1 - } // We need to add one to know if we are violating the limit - ); - if (rejectRemoteExitNodes) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "Remote exit node limit exceeded. Please upgrade your plan or contact us at support@fossorial.io" - ) + if (usage) { + const rejectRemoteExitNodes = await usageService.checkLimitSet( + orgId, + false, + FeatureId.REMOTE_EXIT_NODES, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit ); + + if (rejectRemoteExitNodes) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Remote exit node limit exceeded. Please upgrade your plan or contact us at support@fossorial.io" + ) + ); + } } const secretHash = await hashPassword(secret); diff --git a/tsconfig.json b/tsconfig.json index e32eabd3..0b856fe0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ "#private/*": ["../server/private/*"], "#open/*": ["../server/*"], "#closed/*": ["../server/private/*"], - "#dynamic/*": ["../server/*"] + "#dynamic/*": ["../server/private/*"] }, "plugins": [ { From c7a40d59b7cd050f689b133ea85bf76cf722f956 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 12 Oct 2025 16:34:36 -0700 Subject: [PATCH 215/322] Seperate managed node code to fosrl/pangolin-node --- server/auth/sessions/resource.ts | 26 -- server/db/queries/verifySessionQueries.ts | 172 ------------- server/emails/index.ts | 5 - server/hybridServer.ts | 151 ------------ server/index.ts | 26 +- server/lib/certificates.ts | 67 ----- server/lib/config.ts | 9 +- server/lib/geoip.ts | 33 +-- server/lib/readConfigFile.ts | 22 +- server/lib/remoteProxy.ts | 73 ------ server/lib/tokenManager.ts | 274 --------------------- server/lib/traefik/TraefikConfigManager.ts | 54 ++-- server/private/lib/certificates.ts | 18 +- server/routers/badger/verifySession.ts | 97 ++++---- server/routers/gerbil/getConfig.ts | 11 - server/routers/internal.ts | 43 +--- server/setup/ensureSetupToken.ts | 6 - 17 files changed, 90 insertions(+), 997 deletions(-) delete mode 100644 server/hybridServer.ts delete mode 100644 server/lib/remoteProxy.ts delete mode 100644 server/lib/tokenManager.ts diff --git a/server/auth/sessions/resource.ts b/server/auth/sessions/resource.ts index a378202e..31ab2b38 100644 --- a/server/auth/sessions/resource.ts +++ b/server/auth/sessions/resource.ts @@ -4,9 +4,6 @@ import { resourceSessions, ResourceSession } from "@server/db"; import { db } from "@server/db"; import { eq, and } from "drizzle-orm"; import config from "@server/lib/config"; -import axios from "axios"; -import logger from "@server/logger"; -import { tokenManager } from "@server/lib/tokenManager"; export const SESSION_COOKIE_NAME = config.getRawConfig().server.session_cookie_name; @@ -65,29 +62,6 @@ export async function validateResourceSessionToken( token: string, resourceId: number ): Promise { - if (config.isManagedMode()) { - try { - const response = await axios.post(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/session/validate`, { - token: token - }, await tokenManager.getAuthHeader()); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error validating resource session token in hybrid mode:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error validating resource session token in hybrid mode:", error); - } - return { resourceSession: null }; - } - } - const sessionId = encodeHexLowerCase( sha256(new TextEncoder().encode(token)) ); diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts index 09c465b5..8944a491 100644 --- a/server/db/queries/verifySessionQueries.ts +++ b/server/db/queries/verifySessionQueries.ts @@ -17,10 +17,6 @@ import { users } from "@server/db"; import { and, eq } from "drizzle-orm"; -import axios from "axios"; -import config from "@server/lib/config"; -import logger from "@server/logger"; -import { tokenManager } from "@server/lib/tokenManager"; export type ResourceWithAuth = { resource: Resource | null; @@ -40,30 +36,6 @@ export type UserSessionWithUser = { export async function getResourceByDomain( domain: string ): Promise { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/domain/${domain}`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const [result] = await db .select() .from(resources) @@ -100,30 +72,6 @@ export async function getResourceByDomain( export async function getUserSessionWithUser( userSessionId: string ): Promise { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/session/${userSessionId}`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const [res] = await db .select() .from(sessions) @@ -144,30 +92,6 @@ export async function getUserSessionWithUser( * Get user organization role */ export async function getUserOrgRole(userId: string, orgId: string) { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/org/${orgId}/role`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const userOrgRole = await db .select() .from(userOrgs) @@ -184,30 +108,6 @@ export async function getRoleResourceAccess( resourceId: number, roleId: number ) { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/role/${roleId}/resource/${resourceId}/access`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const roleResourceAccess = await db .select() .from(roleResources) @@ -229,30 +129,6 @@ export async function getUserResourceAccess( userId: string, resourceId: number ) { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/resource/${resourceId}/access`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const userResourceAccess = await db .select() .from(userResources) @@ -273,30 +149,6 @@ export async function getUserResourceAccess( export async function getResourceRules( resourceId: number ): Promise { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/rules`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return []; - } - } - const rules = await db .select() .from(resourceRules) @@ -311,30 +163,6 @@ export async function getResourceRules( export async function getOrgLoginPage( orgId: string ): Promise { - if (config.isManagedMode()) { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/org/${orgId}/login-page`, - await tokenManager.getAuthHeader() - ); - return response.data.data; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - return null; - } - } - const [result] = await db .select() .from(loginPageOrg) diff --git a/server/emails/index.ts b/server/emails/index.ts index 2cdef8a1..42cfa39c 100644 --- a/server/emails/index.ts +++ b/server/emails/index.ts @@ -6,11 +6,6 @@ import logger from "@server/logger"; import SMTPTransport from "nodemailer/lib/smtp-transport"; function createEmailClient() { - if (config.isManagedMode()) { - // LETS NOT WORRY ABOUT EMAILS IN HYBRID - return; - } - const emailConfig = config.getRawConfig().email; if (!emailConfig) { logger.warn( diff --git a/server/hybridServer.ts b/server/hybridServer.ts deleted file mode 100644 index 7e9ce095..00000000 --- a/server/hybridServer.ts +++ /dev/null @@ -1,151 +0,0 @@ -import logger from "@server/logger"; -import config from "@server/lib/config"; -import { createWebSocketClient } from "./routers/ws/client"; -import { addPeer, deletePeer } from "./routers/gerbil/peers"; -import { db, exitNodes } from "./db"; -import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager"; -import { tokenManager } from "./lib/tokenManager"; -import { APP_VERSION } from "./lib/consts"; -import axios from "axios"; - -export async function createHybridClientServer() { - logger.info("Starting hybrid client server..."); - - // Start the token manager - await tokenManager.start(); - - const token = await tokenManager.getToken(); - - const monitor = new TraefikConfigManager(); - - await monitor.start(); - - // Create client - const client = createWebSocketClient( - token, - config.getRawConfig().managed!.endpoint!, - { - reconnectInterval: 5000, - pingInterval: 30000, - pingTimeout: 10000 - } - ); - - // Register message handlers - client.registerHandler("remoteExitNode/peers/add", async (message) => { - const { publicKey, allowedIps } = message.data; - - // TODO: we are getting the exit node twice here - // NOTE: there should only be one gerbil registered so... - const [exitNode] = await db.select().from(exitNodes).limit(1); - await addPeer(exitNode.exitNodeId, { - publicKey: publicKey, - allowedIps: allowedIps || [] - }); - }); - - client.registerHandler("remoteExitNode/peers/remove", async (message) => { - const { publicKey } = message.data; - - // TODO: we are getting the exit node twice here - // NOTE: there should only be one gerbil registered so... - const [exitNode] = await db.select().from(exitNodes).limit(1); - await deletePeer(exitNode.exitNodeId, publicKey); - }); - - // /update-proxy-mapping - client.registerHandler("remoteExitNode/update-proxy-mapping", async (message) => { - try { - const [exitNode] = await db.select().from(exitNodes).limit(1); - if (!exitNode) { - logger.error("No exit node found for proxy mapping update"); - return; - } - - const response = await axios.post(`${exitNode.endpoint}/update-proxy-mapping`, message.data); - logger.info(`Successfully updated proxy mapping: ${response.status}`); - } catch (error) { - // pull data out of the axios error to log - if (axios.isAxiosError(error)) { - logger.error("Error updating proxy mapping:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error updating proxy mapping:", error); - } - } - }); - - // /update-destinations - client.registerHandler("remoteExitNode/update-destinations", async (message) => { - try { - const [exitNode] = await db.select().from(exitNodes).limit(1); - if (!exitNode) { - logger.error("No exit node found for destinations update"); - return; - } - - const response = await axios.post(`${exitNode.endpoint}/update-destinations`, message.data); - logger.info(`Successfully updated destinations: ${response.status}`); - } catch (error) { - // pull data out of the axios error to log - if (axios.isAxiosError(error)) { - logger.error("Error updating destinations:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error updating destinations:", error); - } - } - }); - - client.registerHandler("remoteExitNode/traefik/reload", async (message) => { - await monitor.HandleTraefikConfig(); - }); - - // Listen to connection events - client.on("connect", () => { - logger.info("Connected to WebSocket server"); - client.sendMessage("remoteExitNode/register", { - remoteExitNodeVersion: APP_VERSION - }); - }); - - client.on("disconnect", () => { - logger.info("Disconnected from WebSocket server"); - }); - - client.on("message", (message) => { - logger.info( - `Received message: ${message.type} ${JSON.stringify(message.data)}` - ); - }); - - // Connect to the server - try { - await client.connect(); - logger.info("Connection initiated"); - } catch (error) { - logger.error("Failed to connect:", error); - } - - // Store the ping interval stop function for cleanup if needed - const stopPingInterval = client.sendMessageInterval( - "remoteExitNode/ping", - { timestamp: Date.now() / 1000 }, - 60000 - ); // send every minute - - // Return client and cleanup function for potential use - return { client, stopPingInterval }; -} diff --git a/server/index.ts b/server/index.ts index f4571422..a92968a6 100644 --- a/server/index.ts +++ b/server/index.ts @@ -5,9 +5,15 @@ import { runSetupFunctions } from "./setup"; import { createApiServer } from "./apiServer"; import { createNextServer } from "./nextServer"; import { createInternalServer } from "./internalServer"; -import { ApiKey, ApiKeyOrg, RemoteExitNode, Session, User, UserOrg } from "@server/db"; +import { + ApiKey, + ApiKeyOrg, + RemoteExitNode, + Session, + User, + UserOrg +} from "@server/db"; import { createIntegrationApiServer } from "./integrationApiServer"; -import { createHybridClientServer } from "./hybridServer"; import config from "@server/lib/config"; import { setHostMeta } from "@server/lib/hostMeta"; import { initTelemetryClient } from "./lib/telemetry.js"; @@ -26,16 +32,11 @@ async function startServers() { const apiServer = createApiServer(); const internalServer = createInternalServer(); - let hybridClientServer; let nextServer; - if (config.isManagedMode()) { - hybridClientServer = await createHybridClientServer(); - } else { - nextServer = await createNextServer(); - if (config.getRawConfig().traefik.file_mode) { - const monitor = new TraefikConfigManager(); - await monitor.start(); - } + nextServer = await createNextServer(); + if (config.getRawConfig().traefik.file_mode) { + const monitor = new TraefikConfigManager(); + await monitor.start(); } let integrationServer; @@ -49,8 +50,7 @@ async function startServers() { apiServer, nextServer, internalServer, - integrationServer, - hybridClientServer + integrationServer }; } diff --git a/server/lib/certificates.ts b/server/lib/certificates.ts index 4032d1ec..a6c51c96 100644 --- a/server/lib/certificates.ts +++ b/server/lib/certificates.ts @@ -1,70 +1,3 @@ -import axios from "axios"; -import { tokenManager } from "./tokenManager"; -import logger from "@server/logger"; -import config from "./config"; - -/** - * Get valid certificates for the specified domains - */ -export async function getValidCertificatesForDomainsHybrid(domains: Set): Promise< - Array<{ - id: number; - domain: string; - wildcard: boolean | null; - certFile: string | null; - keyFile: string | null; - expiresAt: number | null; - updatedAt?: number | null; - }> -> { - if (domains.size === 0) { - return []; - } - - const domainArray = Array.from(domains); - - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/certificates/domains`, - { - params: { - domains: domainArray - }, - headers: (await tokenManager.getAuthHeader()).headers - } - ); - - if (response.status !== 200) { - logger.error( - `Failed to fetch certificates for domains: ${response.status} ${response.statusText}`, - { responseData: response.data, domains: domainArray } - ); - return []; - } - - // logger.debug( - // `Successfully retrieved ${response.data.data?.length || 0} certificates for ${domainArray.length} domains` - // ); - - return response.data.data; - } catch (error) { - // pull data out of the axios error to log - if (axios.isAxiosError(error)) { - logger.error("Error getting certificates:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error getting certificates:", error); - } - return []; - } -} - export async function getValidCertificatesForDomains(domains: Set): Promise< Array<{ id: number; diff --git a/server/lib/config.ts b/server/lib/config.ts index 103ea5ae..63053af8 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -101,10 +101,7 @@ export class Config { if (!this.rawConfig) { throw new Error("Config not loaded. Call load() first."); } - if (this.rawConfig.managed) { - // LETS NOT WORRY ABOUT THE SERVER SECRET WHEN MANAGED - return; - } + license.setServerSecret(this.rawConfig.server.secret!); await this.checkKeyStatus(); @@ -157,10 +154,6 @@ export class Config { return false; } - public isManagedMode() { - return typeof this.rawConfig?.managed === "object"; - } - public async checkSupporterKey() { const [key] = await db.select().from(supporterKey).limit(1); diff --git a/server/lib/geoip.ts b/server/lib/geoip.ts index d6252360..ac739fa3 100644 --- a/server/lib/geoip.ts +++ b/server/lib/geoip.ts @@ -1,8 +1,5 @@ import logger from "@server/logger"; import { maxmindLookup } from "@server/db/maxmind"; -import axios from "axios"; -import config from "./config"; -import { tokenManager } from "./tokenManager"; export async function getCountryCodeForIp( ip: string @@ -33,32 +30,4 @@ export async function getCountryCodeForIp( } return; -} - -export async function remoteGetCountryCodeForIp( - ip: string -): Promise { - try { - const response = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/geoip/${ip}`, - await tokenManager.getAuthHeader() - ); - - return response.data.data.countryCode; - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error fetching config in verify session:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error fetching config in verify session:", error); - } - } - - return; -} +} \ No newline at end of file diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index ea872252..0340e21d 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -39,15 +39,6 @@ export const configSchema = z anonymous_usage: true } }), - managed: z - .object({ - name: z.string().optional(), - id: z.string().optional(), - secret: z.string().optional(), - endpoint: z.string().optional().default("https://pangolin.fossorial.io"), - redirect_endpoint: z.string().optional() - }) - .optional(), domains: z .record( z.string(), @@ -320,10 +311,7 @@ export const configSchema = z if (data.flags?.disable_config_managed_domains) { return true; } - // If hybrid is defined, domains are not required - if (data.managed) { - return true; - } + if (keys.length === 0) { return false; } @@ -335,10 +323,6 @@ export const configSchema = z ) .refine( (data) => { - // If hybrid is defined, server secret is not required - if (data.managed) { - return true; - } // If hybrid is not defined, server secret must be defined. If its not defined already then pull it from env if (data.server?.secret === undefined) { data.server.secret = process.env.SERVER_SECRET; @@ -351,10 +335,6 @@ export const configSchema = z ) .refine( (data) => { - // If hybrid is defined, dashboard_url is not required - if (data.managed) { - return true; - } // If hybrid is not defined, dashboard_url must be defined return data.app.dashboard_url !== undefined && data.app.dashboard_url.length > 0; }, diff --git a/server/lib/remoteProxy.ts b/server/lib/remoteProxy.ts deleted file mode 100644 index c9016071..00000000 --- a/server/lib/remoteProxy.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { Router } from "express"; -import axios from "axios"; -import HttpCode from "@server/types/HttpCode"; -import createHttpError from "http-errors"; -import logger from "@server/logger"; -import config from "@server/lib/config"; -import { tokenManager } from "./tokenManager"; - -/** - * Proxy function that forwards requests to the remote cloud server - */ - -export const proxyToRemote = async ( - req: Request, - res: Response, - next: NextFunction, - endpoint: string -): Promise => { - try { - const remoteUrl = `${config.getRawConfig().managed?.endpoint?.replace(/\/$/, '')}/api/v1/${endpoint}`; - - logger.debug(`Proxying request to remote server: ${remoteUrl}`); - - // Forward the request to the remote server - const response = await axios({ - method: req.method as any, - url: remoteUrl, - data: req.body, - headers: { - 'Content-Type': 'application/json', - ...(await tokenManager.getAuthHeader()).headers - }, - params: req.query, - timeout: 30000, // 30 second timeout - validateStatus: () => true // Don't throw on non-2xx status codes - }); - - logger.debug(`Proxy response: ${JSON.stringify(response.data)}`); - - // Forward the response status and data - return res.status(response.status).json(response.data); - - } catch (error) { - logger.error("Error proxying request to remote server:", error); - - if (axios.isAxiosError(error)) { - if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') { - return next( - createHttpError( - HttpCode.SERVICE_UNAVAILABLE, - "Remote server is unavailable" - ) - ); - } - if (error.code === 'ECONNABORTED') { - return next( - createHttpError( - HttpCode.REQUEST_TIMEOUT, - "Request to remote server timed out" - ) - ); - } - } - - return next( - createHttpError( - HttpCode.INTERNAL_SERVER_ERROR, - "Error communicating with remote server" - ) - ); - } -}; \ No newline at end of file diff --git a/server/lib/tokenManager.ts b/server/lib/tokenManager.ts deleted file mode 100644 index 2e0e1118..00000000 --- a/server/lib/tokenManager.ts +++ /dev/null @@ -1,274 +0,0 @@ -import axios from "axios"; -import config from "@server/lib/config"; -import logger from "@server/logger"; - -export interface TokenResponse { - success: boolean; - message?: string; - data: { - token: string; - }; -} - -/** - * Token Manager - Handles automatic token refresh for hybrid server authentication - * - * Usage throughout the application: - * ```typescript - * import { tokenManager } from "@server/lib/tokenManager"; - * - * // Get the current valid token - * const token = await tokenManager.getToken(); - * - * // Force refresh if needed - * await tokenManager.refreshToken(); - * ``` - * - * The token manager automatically refreshes tokens every 24 hours by default - * and is started once in the privateHybridServer.ts file. - */ - -export class TokenManager { - private token: string | null = null; - private refreshInterval: NodeJS.Timeout | null = null; - private isRefreshing: boolean = false; - private refreshIntervalMs: number; - private retryInterval: NodeJS.Timeout | null = null; - private retryIntervalMs: number; - private tokenAvailablePromise: Promise | null = null; - private tokenAvailableResolve: (() => void) | null = null; - - constructor(refreshIntervalMs: number = 24 * 60 * 60 * 1000, retryIntervalMs: number = 5000) { - // Default to 24 hours for refresh, 5 seconds for retry - this.refreshIntervalMs = refreshIntervalMs; - this.retryIntervalMs = retryIntervalMs; - this.setupTokenAvailablePromise(); - } - - /** - * Set up promise that resolves when token becomes available - */ - private setupTokenAvailablePromise(): void { - this.tokenAvailablePromise = new Promise((resolve) => { - this.tokenAvailableResolve = resolve; - }); - } - - /** - * Resolve the token available promise - */ - private resolveTokenAvailable(): void { - if (this.tokenAvailableResolve) { - this.tokenAvailableResolve(); - this.tokenAvailableResolve = null; - } - } - - /** - * Start the token manager - gets initial token and sets up refresh interval - * If initial token fetch fails, keeps retrying every few seconds until successful - */ - async start(): Promise { - logger.info("Starting token manager..."); - - try { - await this.refreshToken(); - this.setupRefreshInterval(); - this.resolveTokenAvailable(); - logger.info("Token manager started successfully"); - } catch (error) { - logger.warn(`Failed to get initial token, will retry in ${this.retryIntervalMs / 1000} seconds:`, error); - this.setupRetryInterval(); - } - } - - /** - * Set up retry interval for initial token acquisition - */ - private setupRetryInterval(): void { - if (this.retryInterval) { - clearInterval(this.retryInterval); - } - - this.retryInterval = setInterval(async () => { - try { - logger.debug("Retrying initial token acquisition"); - await this.refreshToken(); - this.setupRefreshInterval(); - this.clearRetryInterval(); - this.resolveTokenAvailable(); - logger.info("Token manager started successfully after retry"); - } catch (error) { - logger.debug("Token acquisition retry failed, will try again"); - } - }, this.retryIntervalMs); - } - - /** - * Clear retry interval - */ - private clearRetryInterval(): void { - if (this.retryInterval) { - clearInterval(this.retryInterval); - this.retryInterval = null; - } - } - - /** - * Stop the token manager and clear all intervals - */ - stop(): void { - if (this.refreshInterval) { - clearInterval(this.refreshInterval); - this.refreshInterval = null; - } - this.clearRetryInterval(); - logger.info("Token manager stopped"); - } - - /** - * Get the current valid token - */ - - // TODO: WE SHOULD NOT BE GETTING A TOKEN EVERY TIME WE REQUEST IT - async getToken(): Promise { - // If we don't have a token yet, wait for it to become available - if (!this.token && this.tokenAvailablePromise) { - await this.tokenAvailablePromise; - } - - if (!this.token) { - if (this.isRefreshing) { - // Wait for current refresh to complete - await this.waitForRefresh(); - } else { - throw new Error("No valid token available"); - } - } - - if (!this.token) { - throw new Error("No valid token available"); - } - - return this.token; - } - - async getAuthHeader() { - return { - headers: { - Authorization: `Bearer ${await this.getToken()}`, - "X-CSRF-Token": "x-csrf-protection", - } - }; - } - - /** - * Force refresh the token - */ - async refreshToken(): Promise { - if (this.isRefreshing) { - await this.waitForRefresh(); - return; - } - - this.isRefreshing = true; - - try { - const hybridConfig = config.getRawConfig().managed; - - if ( - !hybridConfig?.id || - !hybridConfig?.secret || - !hybridConfig?.endpoint - ) { - throw new Error("Hybrid configuration is not defined"); - } - - const tokenEndpoint = `${hybridConfig.endpoint}/api/v1/auth/remoteExitNode/get-token`; - - const tokenData = { - remoteExitNodeId: hybridConfig.id, - secret: hybridConfig.secret - }; - - logger.debug("Requesting new token from server"); - - const response = await axios.post( - tokenEndpoint, - tokenData, - { - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": "x-csrf-protection" - }, - timeout: 10000 // 10 second timeout - } - ); - - if (!response.data.success) { - throw new Error( - `Failed to get token: ${response.data.message}` - ); - } - - if (!response.data.data.token) { - throw new Error("Received empty token from server"); - } - - this.token = response.data.data.token; - logger.debug("Token refreshed successfully"); - } catch (error) { - if (axios.isAxiosError(error)) { - logger.error("Error updating proxy mapping:", { - message: error.message, - code: error.code, - status: error.response?.status, - statusText: error.response?.statusText, - url: error.config?.url, - method: error.config?.method - }); - } else { - logger.error("Error updating proxy mapping:", error); - } - - throw new Error("Failed to refresh token"); - } finally { - this.isRefreshing = false; - } - } - - /** - * Set up automatic token refresh interval - */ - private setupRefreshInterval(): void { - if (this.refreshInterval) { - clearInterval(this.refreshInterval); - } - - this.refreshInterval = setInterval(async () => { - try { - logger.debug("Auto-refreshing token"); - await this.refreshToken(); - } catch (error) { - logger.error("Failed to auto-refresh token:", error); - } - }, this.refreshIntervalMs); - } - - /** - * Wait for current refresh operation to complete - */ - private async waitForRefresh(): Promise { - return new Promise((resolve) => { - const checkInterval = setInterval(() => { - if (!this.isRefreshing) { - clearInterval(checkInterval); - resolve(); - } - }, 100); - }); - } -} - -// Export a singleton instance for use throughout the application -export const tokenManager = new TokenManager(); diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 435a749f..017678f1 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -6,12 +6,10 @@ import * as yaml from "js-yaml"; import axios from "axios"; import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; -import { tokenManager } from "../tokenManager"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; import { getTraefikConfig } from "#dynamic/lib/traefik"; import { getValidCertificatesForDomains, - getValidCertificatesForDomainsHybrid } from "#dynamic/lib/certificates"; import { sendToExitNode } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; @@ -348,17 +346,8 @@ export class TraefikConfigManager { if (domainsToFetch.size > 0) { // Get valid certificates for domains not covered by wildcards - if (config.isManagedMode()) { - validCertificates = - await getValidCertificatesForDomainsHybrid( - domainsToFetch - ); - } else { - validCertificates = - await getValidCertificatesForDomains( - domainsToFetch - ); - } + validCertificates = + await getValidCertificatesForDomains(domainsToFetch); this.lastCertificateFetch = new Date(); this.lastKnownDomains = new Set(domains); @@ -448,32 +437,15 @@ export class TraefikConfigManager { } | null> { let traefikConfig; try { - if (config.isManagedMode()) { - const resp = await axios.get( - `${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/traefik-config`, - await tokenManager.getAuthHeader() - ); - - if (resp.status !== 200) { - logger.error( - `Failed to fetch traefik config: ${resp.status} ${resp.statusText}`, - { responseData: resp.data } - ); - return null; - } - - traefikConfig = resp.data.data; - } else { - const currentExitNode = await getCurrentExitNodeId(); - // logger.debug(`Fetching traefik config for exit node: ${currentExitNode}`); - traefikConfig = await getTraefikConfig( - // this is called by the local exit node to get its own config - currentExitNode, - config.getRawConfig().traefik.site_types, - build == "oss", // filter out the namespace domains in open source - build != "oss" // generate the login pages on the cloud and hybrid - ); - } + const currentExitNode = await getCurrentExitNodeId(); + // logger.debug(`Fetching traefik config for exit node: ${currentExitNode}`); + traefikConfig = await getTraefikConfig( + // this is called by the local exit node to get its own config + currentExitNode, + config.getRawConfig().traefik.site_types, + build == "oss", // filter out the namespace domains in open source + build != "oss" // generate the login pages on the cloud and hybrid + ); const domains = new Set(); @@ -842,7 +814,9 @@ export class TraefikConfigManager { const lastUpdateStr = fs .readFileSync(lastUpdatePath, "utf8") .trim(); - lastUpdateTime = Math.floor(new Date(lastUpdateStr).getTime() / 1000); + lastUpdateTime = Math.floor( + new Date(lastUpdateStr).getTime() / 1000 + ); } catch { lastUpdateTime = null; } diff --git a/server/private/lib/certificates.ts b/server/private/lib/certificates.ts index 0924daf7..93eb5603 100644 --- a/server/private/lib/certificates.ts +++ b/server/private/lib/certificates.ts @@ -97,20 +97,4 @@ export async function getValidCertificatesForDomains( }); return validCertsDecrypted; -} - -export async function getValidCertificatesForDomainsHybrid( - domains: Set -): Promise< - Array<{ - id: number; - domain: string; - wildcard: boolean | null; - certFile: string | null; - keyFile: string | null; - expiresAt: number | null; - updatedAt?: number | null; - }> -> { - return []; // stub -} +} \ No newline at end of file diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 4a000144..44bc32a5 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -33,7 +33,9 @@ import createHttpError from "http-errors"; import NodeCache from "node-cache"; import { z } from "zod"; import { fromError } from "zod-validation-error"; -import { getCountryCodeForIp, remoteGetCountryCodeForIp } from "@server/lib/geoip"; +import { + getCountryCodeForIp, +} from "@server/lib/geoip"; import { getOrgTierData } from "#dynamic/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import { verifyPassword } from "@server/auth/password"; @@ -106,23 +108,23 @@ export async function verifyResourceSession( const clientIp = requestIp ? (() => { - logger.debug("Request IP:", { requestIp }); - if (requestIp.startsWith("[") && requestIp.includes("]")) { - // if brackets are found, extract the IPv6 address from between the brackets - const ipv6Match = requestIp.match(/\[(.*?)\]/); - if (ipv6Match) { - return ipv6Match[1]; - } - } + logger.debug("Request IP:", { requestIp }); + if (requestIp.startsWith("[") && requestIp.includes("]")) { + // if brackets are found, extract the IPv6 address from between the brackets + const ipv6Match = requestIp.match(/\[(.*?)\]/); + if (ipv6Match) { + return ipv6Match[1]; + } + } - // ivp4 - // split at last colon - const lastColonIndex = requestIp.lastIndexOf(":"); - if (lastColonIndex !== -1) { - return requestIp.substring(0, lastColonIndex); - } - return requestIp; - })() + // ivp4 + // split at last colon + const lastColonIndex = requestIp.lastIndexOf(":"); + if (lastColonIndex !== -1) { + return requestIp.substring(0, lastColonIndex); + } + return requestIp; + })() : undefined; logger.debug("Client IP:", { clientIp }); @@ -137,11 +139,11 @@ export async function verifyResourceSession( const resourceCacheKey = `resource:${cleanHost}`; let resourceData: | { - resource: Resource | null; - pincode: ResourcePincode | null; - password: ResourcePassword | null; - headerAuth: ResourceHeaderAuth | null; - } + resource: Resource | null; + pincode: ResourcePincode | null; + password: ResourcePassword | null; + headerAuth: ResourceHeaderAuth | null; + } | undefined = cache.get(resourceCacheKey); if (!resourceData) { @@ -213,21 +215,21 @@ export async function verifyResourceSession( headers && headers[ config.getRawConfig().server.resource_access_token_headers.id - ] && + ] && headers[ config.getRawConfig().server.resource_access_token_headers.token - ] + ] ) { const accessTokenId = headers[ config.getRawConfig().server.resource_access_token_headers .id - ]; + ]; const accessToken = headers[ config.getRawConfig().server.resource_access_token_headers .token - ]; + ]; const { valid, error, tokenItem } = await verifyResourceAccessToken( { @@ -294,10 +296,17 @@ export async function verifyResourceSession( // check for HTTP Basic Auth header if (headerAuth && clientHeaderAuth) { - if(cache.get(clientHeaderAuth)) { - logger.debug("Resource allowed because header auth is valid (cached)"); + if (cache.get(clientHeaderAuth)) { + logger.debug( + "Resource allowed because header auth is valid (cached)" + ); return allowed(res); - }else if(await verifyPassword(clientHeaderAuth, headerAuth.headerAuthHash)){ + } else if ( + await verifyPassword( + clientHeaderAuth, + headerAuth.headerAuthHash + ) + ) { cache.set(clientHeaderAuth, clientHeaderAuth); logger.debug("Resource allowed because header auth is valid"); return allowed(res); @@ -477,7 +486,11 @@ function extractResourceSessionToken( return latest.token; } -async function notAllowed(res: Response, redirectPath?: string, orgId?: string) { +async function notAllowed( + res: Response, + redirectPath?: string, + orgId?: string +) { let loginPage: LoginPage | null = null; if (orgId) { const { tier } = await getOrgTierData(orgId); // returns null in oss @@ -491,14 +504,11 @@ async function notAllowed(res: Response, redirectPath?: string, orgId?: string) let endpoint: string; if (loginPage && loginPage.domainId && loginPage.fullDomain) { - const secure = config.getRawConfig().app.dashboard_url?.startsWith("https"); + const secure = config + .getRawConfig() + .app.dashboard_url?.startsWith("https"); const method = secure ? "https" : "http"; endpoint = `${method}://${loginPage.fullDomain}`; - } else if (config.isManagedMode()) { - endpoint = - config.getRawConfig().managed?.redirect_endpoint || - config.getRawConfig().managed?.endpoint || - ""; } else { endpoint = config.getRawConfig().app.dashboard_url!; } @@ -803,11 +813,7 @@ async function isIpInGeoIP(ip: string, countryCode: string): Promise { let cachedCountryCode: string | undefined = cache.get(geoIpCacheKey); if (!cachedCountryCode) { - if (config.isManagedMode()) { - cachedCountryCode = await remoteGetCountryCodeForIp(ip); - } else { - cachedCountryCode = await getCountryCodeForIp(ip); // do it locally - } + cachedCountryCode = await getCountryCodeForIp(ip); // do it locally // Cache for longer since IP geolocation doesn't change frequently cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes } @@ -817,7 +823,9 @@ async function isIpInGeoIP(ip: string, countryCode: string): Promise { return cachedCountryCode?.toUpperCase() === countryCode.toUpperCase(); } -function extractBasicAuth(headers: Record | undefined): string | undefined { +function extractBasicAuth( + headers: Record | undefined +): string | undefined { if (!headers || (!headers.authorization && !headers.Authorization)) { return; } @@ -833,8 +841,9 @@ function extractBasicAuth(headers: Record | undefined): string | try { // Extract the base64 encoded credentials return authHeader.slice("Basic ".length); - } catch (error) { - logger.debug("Basic Auth: Failed to decode credentials", { error: error instanceof Error ? error.message : "Unknown error" }); + logger.debug("Basic Auth: Failed to decode credentials", { + error: error instanceof Error ? error.message : "Unknown error" + }); } } diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index 1604dc30..bb581ced 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -9,7 +9,6 @@ import logger from "@server/logger"; import config from "@server/lib/config"; import { fromError } from "zod-validation-error"; import { getAllowedIps } from "../target/helpers"; -import { proxyToRemote } from "@server/lib/remoteProxy"; import { createExitNode } from "#dynamic/routers/gerbil/createExitNode"; // Define Zod schema for request validation @@ -63,16 +62,6 @@ export async function getConfig( ); } - // STOP HERE IN HYBRID MODE - if (config.isManagedMode()) { - req.body = { - ...req.body, - endpoint: exitNode.endpoint, - listenPort: exitNode.listenPort - }; - return proxyToRemote(req, res, next, "hybrid/gerbil/get-config"); - } - const configResponse = await generateGerbilConfig(exitNode); logger.debug("Sending config: ", configResponse); diff --git a/server/routers/internal.ts b/server/routers/internal.ts index 10966bb5..30cde061 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -7,8 +7,6 @@ import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; import * as license from "@server/routers/license"; import * as idp from "@server/routers/idp"; -import { proxyToRemote } from "@server/lib/remoteProxy"; -import config from "@server/lib/config"; import HttpCode from "@server/types/HttpCode"; import { verifyResourceAccess, @@ -51,34 +49,11 @@ internalRouter.get("/idp/:idpId", idp.getIdp); const gerbilRouter = Router(); internalRouter.use("/gerbil", gerbilRouter); -if (config.isManagedMode()) { - // Use proxy router to forward requests to remote cloud server - // Proxy endpoints for each gerbil route - gerbilRouter.post("/receive-bandwidth", (req, res, next) => - proxyToRemote(req, res, next, "hybrid/gerbil/receive-bandwidth") - ); - - gerbilRouter.post("/update-hole-punch", (req, res, next) => - proxyToRemote(req, res, next, "hybrid/gerbil/update-hole-punch") - ); - - gerbilRouter.post("/get-all-relays", (req, res, next) => - proxyToRemote(req, res, next, "hybrid/gerbil/get-all-relays") - ); - - gerbilRouter.post("/get-resolved-hostname", (req, res, next) => - proxyToRemote(req, res, next, `hybrid/gerbil/get-resolved-hostname`) - ); - - // GET CONFIG IS HANDLED IN THE ORIGINAL HANDLER - // SO IT CAN REGISTER THE LOCAL EXIT NODE -} else { - // Use local gerbil endpoints - gerbilRouter.post("/receive-bandwidth", gerbil.receiveBandwidth); - gerbilRouter.post("/update-hole-punch", gerbil.updateHolePunch); - gerbilRouter.post("/get-all-relays", gerbil.getAllRelays); - gerbilRouter.post("/get-resolved-hostname", gerbil.getResolvedHostname); -} +// Use local gerbil endpoints +gerbilRouter.post("/receive-bandwidth", gerbil.receiveBandwidth); +gerbilRouter.post("/update-hole-punch", gerbil.updateHolePunch); +gerbilRouter.post("/get-all-relays", gerbil.getAllRelays); +gerbilRouter.post("/get-resolved-hostname", gerbil.getResolvedHostname); // WE HANDLE THE PROXY INSIDE OF THIS FUNCTION // SO IT REGISTERS THE EXIT NODE LOCALLY AS WELL @@ -90,10 +65,4 @@ internalRouter.use("/badger", badgerRouter); badgerRouter.post("/verify-session", badger.verifyResourceSession); -if (config.isManagedMode()) { - badgerRouter.post("/exchange-session", (req, res, next) => - proxyToRemote(req, res, next, "hybrid/badger/exchange-session") - ); -} else { - badgerRouter.post("/exchange-session", badger.exchangeSession); -} \ No newline at end of file +badgerRouter.post("/exchange-session", badger.exchangeSession); diff --git a/server/setup/ensureSetupToken.ts b/server/setup/ensureSetupToken.ts index 078c99ee..1734b5e6 100644 --- a/server/setup/ensureSetupToken.ts +++ b/server/setup/ensureSetupToken.ts @@ -3,7 +3,6 @@ import { eq } from "drizzle-orm"; import { generateRandomString, RandomReader } from "@oslojs/crypto/random"; import moment from "moment"; import logger from "@server/logger"; -import config from "@server/lib/config"; const random: RandomReader = { read(bytes: Uint8Array): void { @@ -23,11 +22,6 @@ function generateId(length: number): string { } export async function ensureSetupToken() { - if (config.isManagedMode()) { - // LETS NOT WORRY ABOUT THE SERVER SECRET WHEN HYBRID - return; - } - try { // Check if a server admin already exists const [existingAdmin] = await db From 5917881b47db0b8b5e2d21f6deeaba73e64e699f Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 12 Oct 2025 17:06:41 -0700 Subject: [PATCH 216/322] Remove dev image for now #1625 --- .github/workflows/dev-image.yml | 75 --------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 .github/workflows/dev-image.yml diff --git a/.github/workflows/dev-image.yml b/.github/workflows/dev-image.yml deleted file mode 100644 index 1f9ea0ae..00000000 --- a/.github/workflows/dev-image.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Create Dev-Image - -on: - pull_request: - branches: - - main - - dev - types: - - opened - - synchronize - - reopened - -jobs: - docker: - runs-on: ubuntu-latest - - env: - TAG_URL: https://hub.docker.com/r/${{ vars.DOCKER_HUB_REPO }}/tags - TAG: ${{ vars.DOCKER_HUB_REPO }}:dev-pr${{ github.event.pull_request.number }} - TAG_PG: ${{ vars.DOCKER_HUB_REPO }}:postgresql-dev-pr${{ github.event.pull_request.number }} - - steps: - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push Docker image SQLITE - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64 - push: true - tags: ${{ env.TAG }} - cache-from: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache - cache-to: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache,mode=max - build-args: DATABASE=sqlite - - - name: Build and push Docker image PG - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64 - push: true - tags: ${{ env.TAG_PG }} - cache-from: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache-pg - cache-to: type=registry,ref=${{ vars.DOCKER_HUB_REPO }}:buildcache-pg,mode=max - build-args: DATABASE=pg - - - uses: actions/github-script@v8 - with: - script: | - const repoUrl = process.env.TAG_URL; - const tag = process.env.TAG; - const tagPg = process.env.TAG_PG; - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `👋 Thanks for your PR! - Dev images for this PR are now available on [docker hub](${repoUrl}): - - **SQLITE Image:** - \`\`\` - ${tag} - \`\`\` - - **Postgresql Image:** - \`\`\` - ${tagPg} - \`\`\`` - }) - From 4c14ccbb6321a4e2d5dc55ead0ef1c7bb489f00a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 01:33:47 +0000 Subject: [PATCH 217/322] Bump @types/node from 24.7.0 to 24.7.2 in the dev-patch-updates group Bumps the dev-patch-updates group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@types/node` from 24.7.0 to 24.7.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 24.7.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3692de60..64c4cceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.7.0", + "@types/node": "24.7.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.2.2", @@ -6430,9 +6430,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", - "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", + "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 64e7bc9b..70970619 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "24.7.0", + "@types/node": "24.7.2", "@types/nodemailer": "7.0.2", "@types/pg": "8.15.5", "@types/react": "19.2.2", From ccb1f04ad84f30aa52a07fce7dde2468fa4fb51e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 01:38:51 +0000 Subject: [PATCH 218/322] Bump the prod-minor-updates group with 2 updates Bumps the prod-minor-updates group with 2 updates: [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) and [react-hook-form](https://github.com/react-hook-form/react-hook-form). Updates `@aws-sdk/client-s3` from 3.906.0 to 3.908.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.908.0/clients/client-s3) Updates `react-hook-form` from 7.64.0 to 7.65.0 - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.64.0...v7.65.0) --- updated-dependencies: - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.908.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react-hook-form dependency-version: 7.65.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 387 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 195 insertions(+), 196 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3692de60..78673a9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.906.0", + "@aws-sdk/client-s3": "3.908.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -83,7 +83,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-easy-sort": "^1.8.0", - "react-hook-form": "7.64.0", + "react-hook-form": "7.65.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", @@ -379,64 +379,64 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.906.0.tgz", - "integrity": "sha512-6JQGrmQBHjnARQR+HSaj8DvLRbXTpPa8knYi1veT709JHXVkCkNNLKs7ULjVNCpSffRpzVYJn+eONHKj3Y0knQ==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.908.0.tgz", + "integrity": "sha512-c/89iG3of8UEiWbRK014DoHLy8PLLTJtM9IvYLPsvrf83kpV2P/K9WrdbjW4h6e5qt9XPgfNTZ8U607mt7pdmA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/credential-provider-node": "3.906.0", + "@aws-sdk/core": "3.908.0", + "@aws-sdk/credential-provider-node": "3.908.0", "@aws-sdk/middleware-bucket-endpoint": "3.901.0", "@aws-sdk/middleware-expect-continue": "3.901.0", - "@aws-sdk/middleware-flexible-checksums": "3.906.0", + "@aws-sdk/middleware-flexible-checksums": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-location-constraint": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-sdk-s3": "3.906.0", + "@aws-sdk/middleware-sdk-s3": "3.908.0", "@aws-sdk/middleware-ssec": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", - "@aws-sdk/signature-v4-multi-region": "3.906.0", + "@aws-sdk/signature-v4-multi-region": "3.908.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", + "@aws-sdk/util-user-agent-browser": "3.907.0", + "@aws-sdk/util-user-agent-node": "3.908.0", "@aws-sdk/xml-builder": "3.901.0", "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", + "@smithy/core": "^3.15.0", "@smithy/eventstream-serde-browser": "^4.2.0", "@smithy/eventstream-serde-config-resolver": "^4.3.0", "@smithy/eventstream-serde-node": "^4.2.0", - "@smithy/fetch-http-handler": "^5.3.0", - "@smithy/hash-blob-browser": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.1", + "@smithy/hash-blob-browser": "^4.2.1", "@smithy/hash-node": "^4.2.0", "@smithy/hash-stream-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/md5-js": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-endpoint": "^4.3.1", + "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.0", + "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.0", "@smithy/uuid": "^1.1.0", @@ -1001,44 +1001,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.906.0.tgz", - "integrity": "sha512-GGDwjW2cLzoEF5A1tBlZQZXzhlZzuM6cKNbSxUsCcBXtPAX03eb2GKApVy1SzpD03nTJk5T6GicGAm+BzK+lEg==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.908.0.tgz", + "integrity": "sha512-PseFMWvtac+Q+zaY9DMISE+2+glNh0ROJ1yR4gMzeafNHSwkdYu4qcgxLWIOnIodGydBv/tQ6nzHPzExXnUUgw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", + "@aws-sdk/util-user-agent-browser": "3.907.0", + "@aws-sdk/util-user-agent-node": "3.908.0", "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", - "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/core": "^3.15.0", + "@smithy/fetch-http-handler": "^5.3.1", "@smithy/hash-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-endpoint": "^4.3.1", + "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.0", + "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", @@ -1050,21 +1050,21 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.906.0.tgz", - "integrity": "sha512-+FuwAcozee8joVfjwly/8kSFNCvQOkcQYjINUckqBkdjO4iCRfOgSaz+0JMpMcYgVPnnyZv62gJ2g0bj0U+YDQ==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.908.0.tgz", + "integrity": "sha512-okl6FC2cQT1Oidvmnmvyp/IEvqENBagKO0ww4YV5UtBkf0VlhAymCWkZqhovtklsqgq0otag2VRPAgnrMt6nVQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.901.0", "@aws-sdk/xml-builder": "3.901.0", - "@smithy/core": "^3.14.0", + "@smithy/core": "^3.15.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/signature-v4": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -1074,12 +1074,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.906.0.tgz", - "integrity": "sha512-vtMDguMci2aXhkgEqg1iqyQ7vVcafpx9uypksM6FQsNr3Cc/8I6HgfBAja6BuPwkaCn9NoMnG0/iuuOWr8P9dg==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.908.0.tgz", + "integrity": "sha512-FK2YuxoI5CxUflPOIMbVAwDbi6Xvu+2sXopXLmrHc2PfI39M3vmjEoQwYCP8WuQSRb+TbAP3xAkxHjFSBFR35w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/types": "^4.6.0", @@ -1090,20 +1090,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.906.0.tgz", - "integrity": "sha512-L97N2SUkZp03s1LJZ1sCkUaUZ7m9T72faaadn05wyst/iXonSZKPHYMQVWGYhTC2OtRV0FQvBXIAqFZsNGQD0Q==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.908.0.tgz", + "integrity": "sha512-eLbz0geVW9EykujQNnYfR35Of8MreI6pau5K6XDFDUSWO9GF8wqH7CQwbXpXHBlCTHtq4QSLxzorD8U5CROhUw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", - "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/fetch-http-handler": "^5.3.1", "@smithy/node-http-handler": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -1111,18 +1111,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.906.0.tgz", - "integrity": "sha512-r7TbHD80WXo42kTEC5bqa4b87ho3T3yd2VEKo1qbEmOUovocntO8HC3JxHYr0XSeZ82DEYxLARb84akWjabPzg==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.908.0.tgz", + "integrity": "sha512-7Cgnv5wabgFtsgr+Uc/76EfPNGyxmbG8aICn3g3D3iJlcO4uuOZI8a77i0afoDdchZrTC6TG6UusS/NAW6zEoQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/credential-provider-env": "3.906.0", - "@aws-sdk/credential-provider-http": "3.906.0", - "@aws-sdk/credential-provider-process": "3.906.0", - "@aws-sdk/credential-provider-sso": "3.906.0", - "@aws-sdk/credential-provider-web-identity": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/core": "3.908.0", + "@aws-sdk/credential-provider-env": "3.908.0", + "@aws-sdk/credential-provider-http": "3.908.0", + "@aws-sdk/credential-provider-process": "3.908.0", + "@aws-sdk/credential-provider-sso": "3.908.0", + "@aws-sdk/credential-provider-web-identity": "3.908.0", + "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", @@ -1135,17 +1135,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.906.0.tgz", - "integrity": "sha512-xga127vP0rFxiHjEUjLe6Yf4hQ/AZinOF4AqQr/asWQO+/uwh3aH8nXcS4lkpZNygxMHbuNXm7Xg504GKCMlLQ==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.908.0.tgz", + "integrity": "sha512-8OKbykpGw5bdfF/pLTf8YfUi1Kl8o1CTjBqWQTsLOkE3Ho3hsp1eQx8Cz4ttrpv0919kb+lox62DgmAOEmTr1w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.906.0", - "@aws-sdk/credential-provider-http": "3.906.0", - "@aws-sdk/credential-provider-ini": "3.906.0", - "@aws-sdk/credential-provider-process": "3.906.0", - "@aws-sdk/credential-provider-sso": "3.906.0", - "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/credential-provider-env": "3.908.0", + "@aws-sdk/credential-provider-http": "3.908.0", + "@aws-sdk/credential-provider-ini": "3.908.0", + "@aws-sdk/credential-provider-process": "3.908.0", + "@aws-sdk/credential-provider-sso": "3.908.0", + "@aws-sdk/credential-provider-web-identity": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", @@ -1158,12 +1158,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.906.0.tgz", - "integrity": "sha512-P8R4GpDLppe+8mp+SOj1fKaY3AwDULCi/fqMSJjvf8qN6OM+vGGpFP3iXvkjFYyyV+8nRXY+HQCLRoZKpRtzMg==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.908.0.tgz", + "integrity": "sha512-sWnbkGjDPBi6sODUzrAh5BCDpnPw0wpK8UC/hWI13Q8KGfyatAmCBfr+9OeO3+xBHa8N5AskMncr7C4qS846yQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1175,14 +1175,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.906.0.tgz", - "integrity": "sha512-wYljHU7yNEzt7ngZZ21FWh+RlO16gTpWvXyRqlryuCgIWugHD8bl7JphGnUN1md5/v+mCRuGK58JoFGZq+qrjA==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.908.0.tgz", + "integrity": "sha512-WV/aOzuS6ZZhrkPty6TJ3ZG24iS8NXP0m3GuTVuZ5tKi9Guss31/PJ1CrKPRCYGm15CsIjf+mrUxVnNYv9ap5g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.906.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/token-providers": "3.906.0", + "@aws-sdk/client-sso": "3.908.0", + "@aws-sdk/core": "3.908.0", + "@aws-sdk/token-providers": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1194,13 +1194,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.906.0.tgz", - "integrity": "sha512-V9PurepVko8+iyEvI9WAlk5dXJ1uWIW03RPLnNBEmeCqFjjit16HrNaaVvnp9fQbG7CSKSGqK026SjDgtKGKYA==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.908.0.tgz", + "integrity": "sha512-9xWrFn6nWlF5KlV4XYW+7E6F33S3wUUEGRZ/+pgDhkIZd527ycT2nPG2dZ3fWUZMlRmzijP20QIJDqEbbGWe1Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/core": "3.908.0", + "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1245,22 +1245,22 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.906.0.tgz", - "integrity": "sha512-vbOf5Pf2bRjw+Is1OsUKKP88uPKES8/B3c3yq0B72Y4ZgZEDymXIxGvZYPkThLk266PH7eHo+ZneZjkdfz6Zbg==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.908.0.tgz", + "integrity": "sha512-hYGhNBvdfnxhhywYRkesdxIZD8rvhsp2CBci5kCqrR2o5VvEkn5+waUQtkREtkciEpC4ent4fadg7N9XfTKvgQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, @@ -1328,23 +1328,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.906.0.tgz", - "integrity": "sha512-8Ztl5natyVXOvpk/en2j9Bjn2t8vawjbvgcU0/ZF5/JtA1rKSTctRXusICJgCovFHzaAH2MVhA51nnp3d8rViA==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.908.0.tgz", + "integrity": "sha512-23MbAOHsGaD0kTVMVLumaIM1f9vtDImIn2lSvPullbjFHKS4XxfrKuPumtKDzl8gzcux+98XnmfDRKH0fzkOUA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.14.0", + "@smithy/core": "^3.15.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/signature-v4": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, @@ -1367,15 +1367,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.906.0.tgz", - "integrity": "sha512-CMAjq2oCEv5EEvmlFvio8t4KQL2jGORyDQu7oLj4l0a2biPgxbwL3utalbm9yKty1rQM5zKpaa7id7ZG3X1f6A==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.908.0.tgz", + "integrity": "sha512-R0ePEOku72EvyJWy/D0Z5f/Ifpfxa0U9gySO3stpNhOox87XhsILpcIsCHPy0OHz1a7cMoZsF6rMKSzDeCnogQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", - "@smithy/core": "^3.14.0", + "@smithy/core": "^3.15.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" @@ -1385,44 +1385,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.906.0.tgz", - "integrity": "sha512-0/r0bh/9Bm14lVe+jAzQQB2ufq9S4Vd9Wg5rZn8RhrhKl6y/DC1aRzOo2kJTNu5pCbVfQsd/VXLLnkcbOrDy6A==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.908.0.tgz", + "integrity": "sha512-ZxDYrfxOKXNFHLyvJtT96TJ0p4brZOhwRE4csRXrezEVUN+pNgxuem95YvMALPVhlVqON2CTzr8BX+CcBKvX9Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", + "@aws-sdk/core": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", + "@aws-sdk/util-user-agent-browser": "3.907.0", + "@aws-sdk/util-user-agent-node": "3.908.0", "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", - "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/core": "^3.15.0", + "@smithy/fetch-http-handler": "^5.3.1", "@smithy/hash-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-endpoint": "^4.3.1", + "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.0", + "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", @@ -1451,12 +1451,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.906.0.tgz", - "integrity": "sha512-zqxRN8/dSrAaAEi5oXIeScsrbDkS63+ZyaBrkC6bc8Jd/bCvJM6D4LjJJxIOPBNXuF0bNhBIlTmqwtbkiqCwZw==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.908.0.tgz", + "integrity": "sha512-8OodflIzZM2GVuCGiGK6hqwsbfHRDl4kQcEYzHRg9p91H4h5Y876DPvLRkwM7pSC7LKUL0XkKWWVVjwJbp6/Ig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.906.0", + "@aws-sdk/middleware-sdk-s3": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/protocol-http": "^5.3.0", "@smithy/signature-v4": "^5.3.0", @@ -1468,13 +1468,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.906.0.tgz", - "integrity": "sha512-gdxXleCjMUAKnyR/1ksdnv3Fuifr9iuaeEtINRHkwVluwcORabEdOlxW36th2QdkpTTyP1hW35VATz2R6v/i2Q==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.908.0.tgz", + "integrity": "sha512-4SosHWRQ8hj1X2yDenCYHParcCjHcd7S+Mdb/lelwF0JBFCNC+dNCI9ws3cP/dFdZO/AIhJQGUBzEQtieloixw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/core": "3.908.0", + "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -1539,9 +1539,9 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.901.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", - "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", + "version": "3.907.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.907.0.tgz", + "integrity": "sha512-Hus/2YCQmtCEfr4Ls88d07Q99Ex59uvtktiPTV963Q7w7LHuIT/JBjrbwNxtSm2KlJR9PHNdqxwN+fSuNsMGMQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.901.0", @@ -1551,12 +1551,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.906.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.906.0.tgz", - "integrity": "sha512-9Gaglw80E9UZ5FctCp5pZAzT40/vC4Oo0fcNXsfplLkpWqTU+NTdTRMYe3TMZ1/v1/JZKuGUVyHiuo/xLu3NmA==", + "version": "3.908.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.908.0.tgz", + "integrity": "sha512-l6AEaKUAYarcEy8T8NZ+dNZ00VGLs3fW2Cqu1AuPENaSad0/ahEU+VU7MpXS8FhMRGPgplxKVgCTLyTY0Lbssw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", @@ -5374,12 +5374,12 @@ } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.0.tgz", - "integrity": "sha512-HNbGWdyTfSM1nfrZKQjYTvD8k086+M8s1EYkBUdGC++lhxegUp2HgNf5RIt6oOGVvsC26hBCW/11tv8KbwLn/Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -5403,18 +5403,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.14.0.tgz", - "integrity": "sha512-XJ4z5FxvY/t0Dibms/+gLJrI5niRoY0BCmE02fwmPcRYFPI4KI876xaE79YGWIKnEslMbuQPsIEsoU/DXa0DoA==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", + "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -5510,15 +5510,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.0.tgz", - "integrity": "sha512-BG3KSmsx9A//KyIfw+sqNmWFr1YBUr+TwpxFT7yPqAk0yyDh7oSNgzfNH7pS6OC099EGx2ltOULvumCFe8bcgw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", + "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.3.0", "@smithy/querystring-builder": "^4.2.0", "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -5526,13 +5526,13 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.0.tgz", - "integrity": "sha512-MWmrRTPqVKpN8NmxmJPTeQuhewTt8Chf+waB38LXHZoA02+BeWYVQ9ViAwHjug8m7lQb1UWuGqp3JoGDOWvvuA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.1.tgz", + "integrity": "sha512-Os9cg1fTXMwuqbvjemELlf+HB5oEeVyZmYsTbAtDQBmjGyibjmbeeqcaw7xOJLIHrkH/u0wAYabNcN6FRTqMRg==", "license": "Apache-2.0", "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", - "@smithy/chunked-blob-reader-native": "^4.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, @@ -5623,12 +5623,12 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.0.tgz", - "integrity": "sha512-jFVjuQeV8TkxaRlcCNg0GFVgg98tscsmIrIwRFeC74TIUyLE3jmY9xgc1WXrPQYRjQNK3aRoaIk6fhFRGOIoGw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", + "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.14.0", + "@smithy/core": "^3.15.0", "@smithy/middleware-serde": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/shared-ini-file-loader": "^4.3.0", @@ -5642,15 +5642,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.0.tgz", - "integrity": "sha512-yaVBR0vQnOnzex45zZ8ZrPzUnX73eUC8kVFaAAbn04+6V7lPtxn56vZEBBAhgS/eqD6Zm86o6sJs6FuQVoX5qg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", + "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", @@ -5817,17 +5817,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.0.tgz", - "integrity": "sha512-3BDx/aCCPf+kkinYf5QQhdQ9UAGihgOVqI3QO5xQfSaIWvUE4KYLtiGRWsNe1SR7ijXC0QEPqofVp5Sb0zC8xQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", + "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.14.0", - "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/core": "^3.15.0", + "@smithy/middleware-endpoint": "^4.3.1", "@smithy/middleware-stack": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.4.0", + "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -5861,9 +5861,9 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.2.0.tgz", - "integrity": "sha512-+erInz8WDv5KPe7xCsJCp+1WCjSbah9gWcmUXc9NqmhyPx59tf7jqFz+za1tRG1Y5KM1Cy1rWCcGypylFp4mvA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.2.0", @@ -5887,9 +5887,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.0.tgz", - "integrity": "sha512-U8q1WsSZFjXijlD7a4wsDQOvOwV+72iHSfq1q7VD+V75xP/pdtm0WIGuaFJ3gcADDOKj2MIBn4+zisi140HEnQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5924,15 +5924,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.2.0.tgz", - "integrity": "sha512-qzHp7ZDk1Ba4LDwQVCNp90xPGqSu7kmL7y5toBpccuhi3AH7dcVBIT/pUxYcInK4jOy6FikrcTGq5wxcka8UaQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", + "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", - "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { @@ -5940,16 +5939,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.0.tgz", - "integrity": "sha512-FxUHS3WXgx3bTWR6yQHNHHkQHZm/XKIi/CchTnKvBulN6obWpcbzJ6lDToXn+Wp0QlVKd7uYAz2/CTw1j7m+Kg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", + "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^4.3.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.0", + "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, @@ -6011,15 +6010,15 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.4.0.tgz", - "integrity": "sha512-vtO7ktbixEcrVzMRmpQDnw/Ehr9UWjBvSJ9fyAbadKkC4w5Cm/4lMO8cHz8Ysb8uflvQUNRcuux/oNHKPXkffg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", + "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/fetch-http-handler": "^5.3.1", "@smithy/node-http-handler": "^4.3.0", "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", + "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", @@ -16530,9 +16529,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.64.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz", - "integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==", + "version": "7.65.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz", + "integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/package.json b/package.json index 64e7bc9b..49b7e5d0 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.4", - "@aws-sdk/client-s3": "3.906.0", + "@aws-sdk/client-s3": "3.908.0", "@hookform/resolvers": "5.2.2", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -106,7 +106,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-easy-sort": "^1.8.0", - "react-hook-form": "7.64.0", + "react-hook-form": "7.65.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", "reodotdev": "^1.0.0", From e7828a43fa055fc7d53d51b64a9acd436a894442 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 13 Oct 2025 10:32:41 -0700 Subject: [PATCH 219/322] Add flag for generate own certs --- server/lib/traefik/TraefikConfigManager.ts | 151 ++++++----- server/lib/traefik/getTraefikConfig.ts | 94 ++++--- server/private/lib/config.ts | 4 + server/private/lib/rateLimitStore.ts | 2 +- server/private/lib/readConfigFile.ts | 254 +++++++++--------- server/private/lib/redis.ts | 2 +- .../private/lib/traefik/getTraefikConfig.ts | 63 ++--- .../routers/certificates/createCertificate.ts | 18 +- .../resources/[niceId]/rules/page.tsx | 4 +- src/components/ResourceInfoBox.tsx | 61 +++-- src/components/private/AuthPageSettings.tsx | 5 +- src/lib/pullEnv.ts | 6 +- src/lib/types/env.ts | 1 + 13 files changed, 362 insertions(+), 303 deletions(-) diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 017678f1..4898c4ac 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -8,9 +8,7 @@ import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; import { getTraefikConfig } from "#dynamic/lib/traefik"; -import { - getValidCertificatesForDomains, -} from "#dynamic/lib/certificates"; +import { getValidCertificatesForDomains } from "#dynamic/lib/certificates"; import { sendToExitNode } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; @@ -311,84 +309,92 @@ export class TraefikConfigManager { this.lastActiveDomains = new Set(domains); } - // Scan current local certificate state - this.lastLocalCertificateState = - await this.scanLocalCertificateState(); + if ( + process.env.GENERATE_OWN_CERTIFICATES === "true" && + build != "oss" + ) { + // Scan current local certificate state + this.lastLocalCertificateState = + await this.scanLocalCertificateState(); - // Only fetch certificates if needed (domain changes, missing certs, or daily renewal check) - let validCertificates: Array<{ - id: number; - domain: string; - wildcard: boolean | null; - certFile: string | null; - keyFile: string | null; - expiresAt: number | null; - updatedAt?: number | null; - }> = []; + // Only fetch certificates if needed (domain changes, missing certs, or daily renewal check) + let validCertificates: Array<{ + id: number; + domain: string; + wildcard: boolean | null; + certFile: string | null; + keyFile: string | null; + expiresAt: number | null; + updatedAt?: number | null; + }> = []; - if (this.shouldFetchCertificates(domains)) { - // Filter out domains that are already covered by wildcard certificates - const domainsToFetch = new Set(); - for (const domain of domains) { - if ( - !isDomainCoveredByWildcard( - domain, - this.lastLocalCertificateState - ) - ) { - domainsToFetch.add(domain); - } else { - logger.debug( - `Domain ${domain} is covered by existing wildcard certificate, skipping fetch` - ); + if (this.shouldFetchCertificates(domains)) { + // Filter out domains that are already covered by wildcard certificates + const domainsToFetch = new Set(); + for (const domain of domains) { + if ( + !isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { + domainsToFetch.add(domain); + } else { + logger.debug( + `Domain ${domain} is covered by existing wildcard certificate, skipping fetch` + ); + } } - } - if (domainsToFetch.size > 0) { - // Get valid certificates for domains not covered by wildcards - validCertificates = - await getValidCertificatesForDomains(domainsToFetch); - this.lastCertificateFetch = new Date(); - this.lastKnownDomains = new Set(domains); + if (domainsToFetch.size > 0) { + // Get valid certificates for domains not covered by wildcards + validCertificates = + await getValidCertificatesForDomains( + domainsToFetch + ); + this.lastCertificateFetch = new Date(); + this.lastKnownDomains = new Set(domains); - logger.info( - `Fetched ${validCertificates.length} certificates from remote (${domains.size - domainsToFetch.size} domains covered by wildcards)` - ); + logger.info( + `Fetched ${validCertificates.length} certificates from remote (${domains.size - domainsToFetch.size} domains covered by wildcards)` + ); - // Download and decrypt new certificates - await this.processValidCertificates(validCertificates); + // Download and decrypt new certificates + await this.processValidCertificates(validCertificates); + } else { + logger.info( + "All domains are covered by existing wildcard certificates, no fetch needed" + ); + this.lastCertificateFetch = new Date(); + this.lastKnownDomains = new Set(domains); + } + + // Always ensure all existing certificates (including wildcards) are in the config + await this.updateDynamicConfigFromLocalCerts(domains); } else { - logger.info( - "All domains are covered by existing wildcard certificates, no fetch needed" - ); - this.lastCertificateFetch = new Date(); - this.lastKnownDomains = new Set(domains); + const timeSinceLastFetch = this.lastCertificateFetch + ? Math.round( + (Date.now() - + this.lastCertificateFetch.getTime()) / + (1000 * 60) + ) + : 0; + + // logger.debug( + // `Skipping certificate fetch - no changes detected and within 24-hour window (last fetch: ${timeSinceLastFetch} minutes ago)` + // ); + + // Still need to ensure config is up to date with existing certificates + await this.updateDynamicConfigFromLocalCerts(domains); } - // Always ensure all existing certificates (including wildcards) are in the config - await this.updateDynamicConfigFromLocalCerts(domains); - } else { - const timeSinceLastFetch = this.lastCertificateFetch - ? Math.round( - (Date.now() - this.lastCertificateFetch.getTime()) / - (1000 * 60) - ) - : 0; + // Clean up certificates for domains no longer in use + await this.cleanupUnusedCertificates(domains); - // logger.debug( - // `Skipping certificate fetch - no changes detected and within 24-hour window (last fetch: ${timeSinceLastFetch} minutes ago)` - // ); - - // Still need to ensure config is up to date with existing certificates - await this.updateDynamicConfigFromLocalCerts(domains); + // wait 1 second for traefik to pick up the new certificates + await new Promise((resolve) => setTimeout(resolve, 500)); } - // Clean up certificates for domains no longer in use - await this.cleanupUnusedCertificates(domains); - - // wait 1 second for traefik to pick up the new certificates - await new Promise((resolve) => setTimeout(resolve, 500)); - // Write traefik config as YAML to a second dynamic config file if changed await this.writeTraefikDynamicConfig(traefikConfig); @@ -690,7 +696,12 @@ export class TraefikConfigManager { for (const cert of validCertificates) { try { - if (!cert.certFile || !cert.keyFile) { + if ( + !cert.certFile || + !cert.keyFile || + cert.certFile.length === 0 || + cert.keyFile.length === 0 + ) { logger.warn( `Certificate for domain ${cert.domain} is missing cert or key file` ); diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 7e1ce562..2de7b413 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -105,7 +105,12 @@ export async function getTraefikConfig( const priority = row.priority ?? 100; // Create a unique key combining resourceId, path config, and rewrite config - const pathKey = [targetPath, pathMatchType, rewritePath, rewritePathType] + const pathKey = [ + targetPath, + pathMatchType, + rewritePath, + rewritePathType + ] .filter(Boolean) .join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); @@ -120,13 +125,15 @@ export async function getTraefikConfig( ); if (!validation.isValid) { - logger.error(`Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}`); + logger.error( + `Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}` + ); return; } resourcesMap.set(key, { resourceId: row.resourceId, - name: resourceName, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -239,21 +246,18 @@ export async function getTraefikConfig( preferWildcardCert = configDomain.prefer_wildcard_cert; } - let tls = {}; - if (build == "oss") { - tls = { - certResolver: certResolver, - ...(preferWildcardCert - ? { - domains: [ - { - main: wildCard - } - ] - } - : {}) - }; - } + const tls = { + certResolver: certResolver, + ...(preferWildcardCert + ? { + domains: [ + { + main: wildCard + } + ] + } + : {}) + }; const additionalMiddlewares = config.getRawConfig().traefik.additional_middlewares || []; @@ -264,11 +268,12 @@ export async function getTraefikConfig( ]; // Handle path rewriting middleware - if (resource.rewritePath && + if ( + resource.rewritePath && resource.path && resource.pathMatchType && - resource.rewritePathType) { - + resource.rewritePathType + ) { // Create a unique middleware name const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${key}`; @@ -287,7 +292,10 @@ export async function getTraefikConfig( } // the middleware to the config - Object.assign(config_output.http.middlewares, rewriteResult.middlewares); + Object.assign( + config_output.http.middlewares, + rewriteResult.middlewares + ); // middlewares to the router middleware chain if (rewriteResult.chain) { @@ -298,9 +306,13 @@ export async function getTraefikConfig( routerMiddlewares.push(rewriteMiddlewareName); } - logger.debug(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); + logger.debug( + `Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})` + ); } catch (error) { - logger.error(`Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}`); + logger.error( + `Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}` + ); } } @@ -316,7 +328,9 @@ export async function getTraefikConfig( value: string; }[]; } catch (e) { - logger.warn(`Failed to parse headers for resource ${resource.resourceId}: ${e}`); + logger.warn( + `Failed to parse headers for resource ${resource.resourceId}: ${e}` + ); } headersArr.forEach((header) => { @@ -482,14 +496,14 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - cookie: { - name: "p_sticky", // TODO: make this configurable via config.yml like other cookies - secure: resource.ssl, - httpOnly: true - } - } - } + sticky: { + cookie: { + name: "p_sticky", // TODO: make this configurable via config.yml like other cookies + secure: resource.ssl, + httpOnly: true + } + } + } : {}) } }; @@ -590,13 +604,13 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - ipStrategy: { - depth: 0, - sourcePort: true - } - } - } + sticky: { + ipStrategy: { + depth: 0, + sourcePort: true + } + } + } : {}) } }; diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts index a0dbf9a3..8d746960 100644 --- a/server/private/lib/config.ts +++ b/server/private/lib/config.ts @@ -148,6 +148,10 @@ export class PrivateConfig { if (parsedPrivateConfig.stripe?.s3Region) { process.env.S3_REGION = parsedPrivateConfig.stripe.s3Region; } + if (parsedPrivateConfig.flags?.generate_own_certificates) { + process.env.GENERATE_OWN_CERTIFICATES = + parsedPrivateConfig.flags.generate_own_certificates.toString(); + } } this.rawPrivateConfig = parsedPrivateConfig; diff --git a/server/private/lib/rateLimitStore.ts b/server/private/lib/rateLimitStore.ts index 11e1a9d6..20355125 100644 --- a/server/private/lib/rateLimitStore.ts +++ b/server/private/lib/rateLimitStore.ts @@ -17,7 +17,7 @@ import { MemoryStore, Store } from "express-rate-limit"; import RedisStore from "#private/lib/redisStore"; export function createStore(): Store { - if (build != "oss" && privateConfig.getRawPrivateConfig().flags?.enable_redis) { + if (build != "oss" && privateConfig.getRawPrivateConfig().flags.enable_redis) { const rateLimitStore: Store = new RedisStore({ prefix: "api-rate-limit", // Optional: customize Redis key prefix skipFailedRequests: true, // Don't count failed requests diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts index c1847ba5..1228a7d7 100644 --- a/server/private/lib/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -20,141 +20,151 @@ import { build } from "@server/build"; const portSchema = z.number().positive().gt(0).lte(65535); -export const privateConfigSchema = z - .object({ - app: z.object({ +export const privateConfigSchema = z.object({ + app: z + .object({ region: z.string().optional().default("default"), base_domain: z.string().optional() - }).optional().default({ + }) + .optional() + .default({ region: "default" }), - server: z.object({ + server: z + .object({ encryption_key_path: z .string() .optional() .default("./config/encryption.pem") .pipe(z.string().min(8)), resend_api_key: z.string().optional(), - reo_client_id: z.string().optional(), - }).optional().default({ + reo_client_id: z.string().optional() + }) + .optional() + .default({ encryption_key_path: "./config/encryption.pem" }), - redis: z - .object({ - host: z.string(), - port: portSchema, - password: z.string().optional(), - db: z.number().int().nonnegative().optional().default(0), - replicas: z - .array( - z.object({ - host: z.string(), - port: portSchema, - password: z.string().optional(), - db: z.number().int().nonnegative().optional().default(0) + redis: z + .object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0), + replicas: z + .array( + z.object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0) + }) + ) + .optional() + // tls: z + // .object({ + // reject_unauthorized: z + // .boolean() + // .optional() + // .default(true) + // }) + // .optional() + }) + .optional(), + gerbil: z + .object({ + local_exit_node_reachable_at: z + .string() + .optional() + .default("http://gerbil:3003") + }) + .optional() + .default({}), + flags: z + .object({ + enable_redis: z.boolean().optional().default(false), + generate_own_certificates: z.boolean().optional().default(false) + }) + .optional() + .default({}), + branding: z + .object({ + app_name: z.string().optional(), + background_image_path: z.string().optional(), + colors: z + .object({ + light: colorsSchema.optional(), + dark: colorsSchema.optional() + }) + .optional(), + logo: z + .object({ + light_path: z.string().optional(), + dark_path: z.string().optional(), + auth_page: z + .object({ + width: z.number().optional(), + height: z.number().optional() }) - ) - .optional() - // tls: z - // .object({ - // reject_unauthorized: z - // .boolean() - // .optional() - // .default(true) - // }) - // .optional() - }) - .optional(), - gerbil: z - .object({ - local_exit_node_reachable_at: z.string().optional().default("http://gerbil:3003") - }) - .optional() - .default({}), - flags: z - .object({ - enable_redis: z.boolean().optional(), - }) - .optional(), - branding: z - .object({ - app_name: z.string().optional(), - background_image_path: z.string().optional(), - colors: z - .object({ - light: colorsSchema.optional(), - dark: colorsSchema.optional() - }) - .optional(), - logo: z - .object({ - light_path: z.string().optional(), - dark_path: z.string().optional(), - auth_page: z - .object({ - width: z.number().optional(), - height: z.number().optional() - }) - .optional(), - navbar: z - .object({ - width: z.number().optional(), - height: z.number().optional() - }) - .optional() - }) - .optional(), - favicon_path: z.string().optional(), - footer: z - .array( - z.object({ - text: z.string(), - href: z.string().optional() + .optional(), + navbar: z + .object({ + width: z.number().optional(), + height: z.number().optional() }) - ) - .optional(), - login_page: z - .object({ - subtitle_text: z.string().optional(), - title_text: z.string().optional() + .optional() + }) + .optional(), + favicon_path: z.string().optional(), + footer: z + .array( + z.object({ + text: z.string(), + href: z.string().optional() }) - .optional(), - signup_page: z - .object({ - subtitle_text: z.string().optional(), - title_text: z.string().optional() - }) - .optional(), - resource_auth_page: z - .object({ - show_logo: z.boolean().optional(), - hide_powered_by: z.boolean().optional(), - title_text: z.string().optional(), - subtitle_text: z.string().optional() - }) - .optional(), - emails: z - .object({ - signature: z.string().optional(), - colors: z - .object({ - primary: z.string().optional() - }) - .optional() - }) - .optional() - }) - .optional(), - stripe: z - .object({ - secret_key: z.string(), - webhook_secret: z.string(), - s3Bucket: z.string(), - s3Region: z.string().default("us-east-1"), - localFilePath: z.string() - }) - .optional(), - }); + ) + .optional(), + login_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + signup_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + resource_auth_page: z + .object({ + show_logo: z.boolean().optional(), + hide_powered_by: z.boolean().optional(), + title_text: z.string().optional(), + subtitle_text: z.string().optional() + }) + .optional(), + emails: z + .object({ + signature: z.string().optional(), + colors: z + .object({ + primary: z.string().optional() + }) + .optional() + }) + .optional() + }) + .optional(), + stripe: z + .object({ + secret_key: z.string(), + webhook_secret: z.string(), + s3Bucket: z.string(), + s3Region: z.string().default("us-east-1"), + localFilePath: z.string() + }) + .optional() +}); export function readPrivateConfigFile() { if (build == "oss") { @@ -182,9 +192,7 @@ export function readPrivateConfigFile() { } if (!environment) { - throw new Error( - "No private configuration file found." - ); + throw new Error("No private configuration file found."); } return environment; diff --git a/server/private/lib/redis.ts b/server/private/lib/redis.ts index e74874f2..324a6a74 100644 --- a/server/private/lib/redis.ts +++ b/server/private/lib/redis.ts @@ -46,7 +46,7 @@ class RedisManager { this.isEnabled = false; return; } - this.isEnabled = privateConfig.getRawPrivateConfig().flags?.enable_redis || false; + this.isEnabled = privateConfig.getRawPrivateConfig().flags.enable_redis || false; if (this.isEnabled) { this.initializeClients(); } diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 161ef48c..dffaf807 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -21,11 +21,10 @@ import { } from "@server/db"; import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; import logger from "@server/logger"; -import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; -import { build } from "@server/build"; import { sanitize } from "@server/lib/traefik/utils"; +import privateConfig from "#private/lib/config"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; @@ -79,7 +78,7 @@ export async function getTraefikConfig( path: targets.path, pathMatchType: targets.pathMatchType, priority: targets.priority, - + // Site fields siteId: sites.siteId, siteType: sites.type, @@ -234,12 +233,13 @@ export async function getTraefikConfig( continue; } - if (resource.certificateStatus !== "valid") { - logger.debug( - `Resource ${resource.resourceId} has certificate stats ${resource.certificateStats}` - ); - continue; - } + // TODO: for now dont filter it out because if you have multiple domain ids and one is failed it causes all of them to fail + // if (resource.certificateStatus !== "valid" && privateConfig.getRawPrivateConfig().flags.generate_own_certificates) { + // logger.debug( + // `Resource ${resource.resourceId} has certificate stats ${resource.certificateStats}` + // ); + // continue; + // } // add routers and services empty objects if they don't exist if (!config_output.http.routers) { @@ -264,18 +264,21 @@ export async function getTraefikConfig( const configDomain = config.getDomain(resource.domainId); - let certResolver: string, preferWildcardCert: boolean; - if (!configDomain) { - certResolver = config.getRawConfig().traefik.cert_resolver; - preferWildcardCert = - config.getRawConfig().traefik.prefer_wildcard_cert; - } else { - certResolver = configDomain.cert_resolver; - preferWildcardCert = configDomain.prefer_wildcard_cert; - } - let tls = {}; - if (build == "oss") { + if ( + !privateConfig.getRawPrivateConfig().flags + .generate_own_certificates + ) { + let certResolver: string, preferWildcardCert: boolean; + if (!configDomain) { + certResolver = config.getRawConfig().traefik.cert_resolver; + preferWildcardCert = + config.getRawConfig().traefik.prefer_wildcard_cert; + } else { + certResolver = configDomain.cert_resolver; + preferWildcardCert = configDomain.prefer_wildcard_cert; + } + tls = { certResolver: certResolver, ...(preferWildcardCert @@ -419,7 +422,7 @@ export async function getTraefikConfig( return ( (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + .filter((target: TargetWithSite) => { if (!target.enabled) { return false; } @@ -440,7 +443,7 @@ export async function getTraefikConfig( ) { return false; } - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { if ( !target.internalPort || !target.method || @@ -448,10 +451,10 @@ export async function getTraefikConfig( ) { return false; } - } - return true; - }) - .map((target: TargetWithSite) => { + } + return true; + }) + .map((target: TargetWithSite) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -459,14 +462,14 @@ export async function getTraefikConfig( return { url: `${target.method}://${target.ip}:${target.port}` }; - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; return { url: `${target.method}://${ip}:${target.internalPort}` }; - } - }) + } + }) // filter out duplicates .filter( (v, i, a) => @@ -709,4 +712,4 @@ export async function getTraefikConfig( } return config_output; -} \ No newline at end of file +} diff --git a/server/private/routers/certificates/createCertificate.ts b/server/private/routers/certificates/createCertificate.ts index 210878ef..463a7b88 100644 --- a/server/private/routers/certificates/createCertificate.ts +++ b/server/private/routers/certificates/createCertificate.ts @@ -15,15 +15,19 @@ import { Certificate, certificates, db, domains } from "@server/db"; import logger from "@server/logger"; import { Transaction } from "@server/db"; import { eq, or, and, like } from "drizzle-orm"; -import { build } from "@server/build"; +import privateConfig from "#private/lib/config"; /** * Checks if a certificate exists for the given domain. * If not, creates a new certificate in 'pending' state. * Wildcard certs cover subdomains. */ -export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { - if (build !== "saas") { +export async function createCertificate( + domainId: string, + domain: string, + trx: Transaction | typeof db +) { + if (!privateConfig.getRawPrivateConfig().flags.generate_own_certificates) { return; } @@ -39,7 +43,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T let existing: Certificate[] = []; if (domainRecord.type == "ns") { - const domainLevelDown = domain.split('.').slice(1).join('.'); + const domainLevelDown = domain.split(".").slice(1).join("."); existing = await trx .select() .from(certificates) @@ -49,7 +53,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T eq(certificates.wildcard, true), // only NS domains can have wildcard certs or( eq(certificates.domain, domain), - eq(certificates.domain, domainLevelDown), + eq(certificates.domain, domainLevelDown) ) ) ); @@ -67,9 +71,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T } if (existing.length > 0) { - logger.info( - `Certificate already exists for domain ${domain}` - ); + logger.info(`Certificate already exists for domain ${domain}`); return; } diff --git a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx index 8d5ad7d3..b8459293 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx @@ -117,8 +117,8 @@ export default function ResourceRules(props: { const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] = useState(false); const router = useRouter(); const t = useTranslations(); - const env = useEnvContext(); - const isMaxmindAvailable = env.env.server.maxmind_db_path && env.env.server.maxmind_db_path.length > 0; + const { env } = useEnvContext(); + const isMaxmindAvailable = env.server.maxmind_db_path && env.server.maxmind_db_path.length > 0; const RuleAction = { ACCEPT: t('alwaysAllow'), diff --git a/src/components/ResourceInfoBox.tsx b/src/components/ResourceInfoBox.tsx index 579b2c31..ebb5fac7 100644 --- a/src/components/ResourceInfoBox.tsx +++ b/src/components/ResourceInfoBox.tsx @@ -13,12 +13,14 @@ import { import { useTranslations } from "next-intl"; import { build } from "@server/build"; import CertificateStatus from "@app/components/private/CertificateStatus"; -import { toUnicode } from 'punycode'; +import { toUnicode } from "punycode"; +import { useEnvContext } from "@app/hooks/useEnvContext"; type ResourceInfoBoxType = {}; -export default function ResourceInfoBox({ }: ResourceInfoBoxType) { +export default function ResourceInfoBox({}: ResourceInfoBoxType) { const { resource, authInfo } = useResourceContext(); + const { env } = useEnvContext(); const t = useTranslations(); @@ -28,7 +30,13 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {/* 4 cols because of the certs */} - + {resource.http ? ( <> @@ -37,9 +45,9 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {authInfo.password || - authInfo.pincode || - authInfo.sso || - authInfo.whitelist ? ( + authInfo.pincode || + authInfo.sso || + authInfo.whitelist ? (
{t("protected")} @@ -126,25 +134,28 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {/* */} {/* */} {/* Certificate Status Column */} - {resource.http && resource.domainId && resource.fullDomain && build != "oss" && ( - - - {t("certificateStatus", { - defaultValue: "Certificate" - })} - - - - - - )} + {resource.http && + resource.domainId && + resource.fullDomain && + build != "oss" && ( + + + {t("certificateStatus", { + defaultValue: "Certificate" + })} + + + + + + )} {t("visibility")} diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 8da85468..b944ab7b 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -80,6 +80,7 @@ const AuthPageSettings = forwardRef( const api = createApiClient(useEnvContext()); const router = useRouter(); const t = useTranslations(); + const { env } = useEnvContext(); const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; @@ -435,8 +436,8 @@ const AuthPageSettings = forwardRef(
{/* Certificate Status */} - {(build !== "saas" || - (build === "saas" && subscribed)) && + {( + (env.flags.generateOwnCertificates && subscribed)) && loginPage?.domainId && loginPage?.fullDomain && !hasUnsavedChanges && ( diff --git a/src/lib/pullEnv.ts b/src/lib/pullEnv.ts index dfe22a87..c099a3fb 100644 --- a/src/lib/pullEnv.ts +++ b/src/lib/pullEnv.ts @@ -48,7 +48,11 @@ export function pullEnv(): Env { enableClients: process.env.FLAGS_ENABLE_CLIENTS === "true" ? true : false, hideSupporterKey: - process.env.HIDE_SUPPORTER_KEY === "true" ? true : false + process.env.HIDE_SUPPORTER_KEY === "true" ? true : false, + generateOwnCertificates: + process.env.GENERATE_OWN_CERTIFICATES === "true" + ? true + : false }, branding: { diff --git a/src/lib/types/env.ts b/src/lib/types/env.ts index 2c3c2479..179d390f 100644 --- a/src/lib/types/env.ts +++ b/src/lib/types/env.ts @@ -28,6 +28,7 @@ export type Env = { disableBasicWireguardSites: boolean; enableClients: boolean; hideSupporterKey: boolean; + generateOwnCertificates: boolean; }, branding: { appName?: string; From 37ceabdf5dde2b0d8cdd3a276a4a47bf9de3108a Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 13 Oct 2025 10:41:10 -0700 Subject: [PATCH 220/322] add enterprise license system --- drizzle.pg.config.ts | 3 +- drizzle.sqlite.config.ts | 3 +- messages/bg-BG.json | 2 - messages/cs-CZ.json | 2 - messages/de-DE.json | 2 - messages/en-US.json | 112 +- messages/es-ES.json | 2 - messages/fr-FR.json | 2 - messages/it-IT.json | 2 - messages/ko-KR.json | 2 - messages/nb-NO.json | 2 - messages/nl-NL.json | 2 - messages/pl-PL.json | 2 - messages/pt-PT.json | 2 - messages/ru-RU.json | 2 - messages/tr-TR.json | 2 - messages/zh-CN.json | 2 - server/db/pg/schema.ts | 2 +- server/db/sqlite/schema.ts | 2 +- server/lib/config.ts | 13 +- server/lib/readConfigFile.ts | 278 ++-- server/lib/telemetry.ts | 42 +- server/license/license.ts | 467 +----- server/middlewares/index.ts | 3 +- server/private/lib/config.ts | 10 +- server/private/lib/readConfigFile.ts | 6 +- server/private/license/license.ts | 459 ++++++ server/{ => private}/license/licenseJwt.ts | 13 + .../middlewares/verifyValidLicense.ts | 7 +- server/private/routers/external.ts | 163 +- .../generatedLicense/generateNewLicense.ts | 91 ++ .../private/routers/generatedLicense/index.ts | 2 + .../generatedLicense/listGeneratedLicenses.ts | 83 + server/private/routers/internal.ts | 3 + .../routers/license/activateLicense.ts | 3 +- .../routers/license/deleteLicenseKey.ts | 5 +- .../routers/license/getLicenseStatus.ts | 3 +- server/{ => private}/routers/license/index.ts | 0 .../routers/license/listLicenseKeys.ts | 3 +- .../routers/license/recheckStatus.ts | 3 +- server/routers/external.ts | 25 - server/routers/idp/createOidcIdp.ts | 1 - server/routers/idp/updateOidcIdp.ts | 1 - server/routers/internal.ts | 5 +- .../supporterKey/isSupporterKeyVisible.ts | 10 +- src/app/[orgId]/layout.tsx | 8 +- .../settings/(private)/billing/layout.tsx | 7 - .../[orgId]/settings/(private)/idp/page.tsx | 5 +- .../settings/(private)/license/layout.tsx | 42 + .../settings/(private)/license/page.tsx | 25 + .../settings/access/users/create/page.tsx | 479 +++--- src/app/[orgId]/settings/clients/page.tsx | 7 +- src/app/[orgId]/settings/general/page.tsx | 14 +- .../[niceId]/authentication/page.tsx | 9 +- src/app/admin/license/layout.tsx | 17 + src/app/admin/license/page.tsx | 297 ++-- src/app/admin/managed/page.tsx | 180 --- src/app/auth/(private)/org/page.tsx | 25 +- src/app/auth/layout.tsx | 66 - src/app/auth/resource/[resourceGuid]/page.tsx | 12 +- src/app/layout.tsx | 20 +- src/app/navigation.tsx | 27 +- src/components/GenerateLicenseKeyForm.tsx | 1384 +++++++++++++++++ src/components/GenerateLicenseKeysTable.tsx | 192 +++ src/components/LayoutSidebar.tsx | 87 +- src/components/LicenseKeysDataTable.tsx | 42 +- src/components/LicenseViolation.tsx | 24 - src/components/SidebarLicenseButton.tsx | 54 + src/components/SidebarNav.tsx | 33 +- src/components/SitesTable.tsx | 2 +- src/components/StrategySelect.tsx | 4 +- src/components/private/AuthPageSettings.tsx | 867 ++++++----- src/contexts/subscriptionStatusContext.ts | 2 + src/providers/LicenseStatusProvider.tsx | 7 - src/providers/SubscriptionStatusProvider.tsx | 28 +- tsconfig.json | 2 +- 76 files changed, 3886 insertions(+), 1931 deletions(-) create mode 100644 server/private/license/license.ts rename server/{ => private}/license/licenseJwt.ts (88%) rename server/{ => private}/middlewares/verifyValidLicense.ts (81%) create mode 100644 server/private/routers/generatedLicense/generateNewLicense.ts create mode 100644 server/private/routers/generatedLicense/index.ts create mode 100644 server/private/routers/generatedLicense/listGeneratedLicenses.ts rename server/{ => private}/routers/license/activateLicense.ts (93%) rename server/{ => private}/routers/license/deleteLicenseKey.ts (92%) rename server/{ => private}/routers/license/getLicenseStatus.ts (89%) rename server/{ => private}/routers/license/index.ts (100%) rename server/{ => private}/routers/license/listLicenseKeys.ts (89%) rename server/{ => private}/routers/license/recheckStatus.ts (91%) create mode 100644 src/app/[orgId]/settings/(private)/license/layout.tsx create mode 100644 src/app/[orgId]/settings/(private)/license/page.tsx create mode 100644 src/app/admin/license/layout.tsx delete mode 100644 src/app/admin/managed/page.tsx create mode 100644 src/components/GenerateLicenseKeyForm.tsx create mode 100644 src/components/GenerateLicenseKeysTable.tsx create mode 100644 src/components/SidebarLicenseButton.tsx diff --git a/drizzle.pg.config.ts b/drizzle.pg.config.ts index 8fc99161..f6dbb665 100644 --- a/drizzle.pg.config.ts +++ b/drizzle.pg.config.ts @@ -1,10 +1,9 @@ import { defineConfig } from "drizzle-kit"; import path from "path"; -import { build } from "@server/build"; const schema = [ path.join("server", "db", "pg", "schema.ts"), - path.join("server", "db", "pg", "pSchema.ts") + path.join("server", "db", "pg", "privateSchema.ts") ]; export default defineConfig({ diff --git a/drizzle.sqlite.config.ts b/drizzle.sqlite.config.ts index b8679aa9..df635931 100644 --- a/drizzle.sqlite.config.ts +++ b/drizzle.sqlite.config.ts @@ -1,11 +1,10 @@ -import { build } from "@server/build"; import { APP_PATH } from "@server/lib/consts"; import { defineConfig } from "drizzle-kit"; import path from "path"; const schema = [ path.join("server", "db", "sqlite", "schema.ts"), - path.join("server", "db", "sqlite", "pSchema.ts") + path.join("server", "db", "sqlite", "privateSchema.ts") ]; export default defineConfig({ diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 9cc9e9f0..74bf5d87 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Администратор на сървър - Панголин", "licenseTierProfessional": "Професионален лиценз", "licenseTierEnterprise": "Предприятие лиценз", - "licenseTierCommercial": "Търговски лиценз", "licensed": "Лицензиран", "yes": "Да", "no": "Не", @@ -1084,7 +1083,6 @@ "navbar": "Навигационно меню", "navbarDescription": "Главно навигационно меню за приложението", "navbarDocsLink": "Документация", - "commercialEdition": "Търговско издание", "otpErrorEnable": "Не може да се активира 2FA", "otpErrorEnableDescription": "Възникна грешка при активиране на 2FA", "otpSetupCheckCode": "Моля, въведете 6-цифрен код", diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index f1106fb8..9d573022 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Správce serveru - Pangolin", "licenseTierProfessional": "Profesionální licence", "licenseTierEnterprise": "Podniková licence", - "licenseTierCommercial": "Obchodní licence", "licensed": "Licencováno", "yes": "Ano", "no": "Ne", @@ -1084,7 +1083,6 @@ "navbar": "Navigation Menu", "navbarDescription": "Hlavní navigační menu aplikace", "navbarDocsLink": "Dokumentace", - "commercialEdition": "Obchodní vydání", "otpErrorEnable": "2FA nelze povolit", "otpErrorEnableDescription": "Došlo k chybě při povolování 2FA", "otpSetupCheckCode": "Zadejte 6místný kód", diff --git a/messages/de-DE.json b/messages/de-DE.json index 5ac9fc02..eaca92bf 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Server-Admin - Pangolin", "licenseTierProfessional": "Professional Lizenz", "licenseTierEnterprise": "Enterprise Lizenz", - "licenseTierCommercial": "Gewerbliche Lizenz", "licensed": "Lizenziert", "yes": "Ja", "no": "Nein", @@ -1084,7 +1083,6 @@ "navbar": "Navigationsmenü", "navbarDescription": "Hauptnavigationsmenü für die Anwendung", "navbarDocsLink": "Dokumentation", - "commercialEdition": "Kommerzielle Edition", "otpErrorEnable": "2FA konnte nicht aktiviert werden", "otpErrorEnableDescription": "Beim Aktivieren der 2FA ist ein Fehler aufgetreten", "otpSetupCheckCode": "Bitte geben Sie einen 6-stelligen Code ein", diff --git a/messages/en-US.json b/messages/en-US.json index 9435b5d7..b49f40a9 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -715,7 +715,7 @@ "pangolinServerAdmin": "Server Admin - Pangolin", "licenseTierProfessional": "Professional License", "licenseTierEnterprise": "Enterprise License", - "licenseTierCommercial": "Commercial License", + "licenseTierPersonal": "Personal License", "licensed": "Licensed", "yes": "Yes", "no": "No", @@ -750,7 +750,7 @@ "idpDisplayName": "A display name for this identity provider", "idpAutoProvisionUsers": "Auto Provision Users", "idpAutoProvisionUsersDescription": "When enabled, users will be automatically created in the system upon first login with the ability to map users to roles and organizations.", - "licenseBadge": "Professional", + "licenseBadge": "EE", "idpType": "Provider Type", "idpTypeDescription": "Select the type of identity provider you want to configure", "idpOidcConfigure": "OAuth2/OIDC Configuration", @@ -1084,7 +1084,6 @@ "navbar": "Navigation Menu", "navbarDescription": "Main navigation menu for the application", "navbarDocsLink": "Documentation", - "commercialEdition": "Commercial Edition", "otpErrorEnable": "Unable to enable 2FA", "otpErrorEnableDescription": "An error occurred while enabling 2FA", "otpSetupCheckCode": "Please enter a 6-digit code", @@ -1140,7 +1139,7 @@ "sidebarAllUsers": "All Users", "sidebarIdentityProviders": "Identity Providers", "sidebarLicense": "License", - "sidebarClients": "Clients (Beta)", + "sidebarClients": "Clients", "sidebarDomains": "Domains", "enableDockerSocket": "Enable Docker Blueprint", "enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.", @@ -1216,7 +1215,7 @@ "refreshError": "Failed to refresh data", "verified": "Verified", "pending": "Pending", - "sidebarBilling": "Billing", + "sidebarBilling": "Payment & Billing", "billing": "Billing", "orgBillingDescription": "Manage your billing information and subscriptions", "github": "GitHub", @@ -1740,5 +1739,106 @@ "resourceHeaderAuthSetupTitle": "Set Header Authentication", "resourceHeaderAuthSetupTitleDescription": "Set the basic auth credentials (username and password) to protect this resource with HTTP Header Authentication. Leave both fields blank to remove existing header authentication.", "resourceHeaderAuthSubmit": "Set Header Authentication", - "actionSetResourceHeaderAuth": "Set Header Authentication" + "actionSetResourceHeaderAuth": "Set Header Authentication", + "enterpriseEdition": "Enterprise Edition", + "unlicensed": "Unlicensed", + "beta": "Beta", + "manageClients": "Manage Clients", + "manageClientsDescription": "Clients are devices that can connect to your sites", + "licenseTableValidUntil": "Valid Until", + "saasLicenseKeysSettingsTitle": "Enterprise Licenses", + "saasLicenseKeysSettingsDescription": "Generate and manage Enterprise license keys for self-hosted Pangolin instances", + "sidebarEnterpriseLicenses": "Enterprise Licenses", + "generateLicenseKey": "Generate License Key", + "generateLicenseKeyForm": { + "validation": { + "emailRequired": "Please enter a valid email address", + "useCaseTypeRequired": "Please select a use case type", + "firstNameRequired": "First name is required", + "lastNameRequired": "Last name is required", + "primaryUseRequired": "Please describe your primary use", + "jobTitleRequiredBusiness": "Job title is required for business use", + "industryRequiredBusiness": "Industry is required for business use", + "stateProvinceRegionRequired": "State/Province/Region is required", + "postalZipCodeRequired": "Postal/ZIP Code is required", + "companyNameRequiredBusiness": "Company name is required for business use", + "countryOfResidenceRequiredBusiness": "Country of residence is required for business use", + "countryRequiredPersonal": "Country is required for personal use", + "agreeToTermsRequired": "You must agree to the terms", + "complianceConfirmationRequired": "You must confirm compliance with the Fossorial Commercial License" + }, + "useCaseOptions": { + "personal": { + "title": "Personal Use", + "description": "For individual, non-commercial use such as learning, personal projects, or experimentation." + }, + "business": { + "title": "Business Use", + "description": "For use within organizations, companies, or any commercial or revenue-generating activities." + } + }, + "steps": { + "emailLicenseType": { + "title": "Email & License Type", + "description": "Enter your email and choose your license type" + }, + "personalInformation": { + "title": "Personal Information", + "description": "Tell us about yourself" + }, + "contactInformation": { + "title": "Contact Information", + "description": "Your contact details" + }, + "termsGenerate": { + "title": "Terms & Generate", + "description": "Review and accept terms to generate your license" + } + }, + "alerts": { + "commercialUseDisclosure": { + "title": "Usage Disclosure", + "description": "Select the license type that accurately reflects your intended use. Pangolin Enterprise is free for personal, non-commercial use only — limited to individuals generating less than $100,000 USD annually and not used in primary employment, business operations, or other commercial environments. All business or revenue-generating use requires a valid Business License and payment of the applicable licensing fee. Both personal and business users must comply with the Fossorial Commercial License Terms." + }, + "trialPeriodInformation": { + "title": "Trial Period Information", + "description": "This license key is valid for 7 days as a trial period. For a long-term license key, please contact sales@fossorial.io." + } + }, + "form": { + "useCaseQuestion": "Are you using Pangolin for personal or business use?", + "firstName": "First Name", + "lastName": "Last Name", + "jobTitle": "Job Title", + "primaryUseQuestion": "What do you primarily plan to use Pangolin for?", + "industryQuestion": "What is your industry?", + "prospectiveUsersQuestion": "How many prospective users do you expect to have?", + "prospectiveSitesQuestion": "How many prospective sites (tunnels) do you expect to have?", + "companyName": "Company name", + "countryOfResidence": "Country of residence", + "stateProvinceRegion": "State / Province / Region", + "postalZipCode": "Postal / ZIP Code", + "companyWebsite": "Company website", + "companyPhoneNumber": "Company phone number", + "country": "Country", + "phoneNumberOptional": "Phone number (optional)", + "complianceConfirmation": "I confirm that I am in compliance with the Fossorial Commercial License and that reporting inaccurate information or misidentifying use of the product is a violation of the license." + }, + "buttons": { + "close": "Close", + "previous": "Previous", + "next": "Next", + "generateLicenseKey": "Generate License Key" + }, + "toasts": { + "success": { + "title": "License key generated successfully", + "description": "Your license key has been generated and is ready to use." + }, + "error": { + "title": "Failed to generate license key", + "description": "An error occurred while generating the license key." + } + } + } } diff --git a/messages/es-ES.json b/messages/es-ES.json index a1b92f8b..2e7cf00a 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Admin Servidor - Pangolin", "licenseTierProfessional": "Licencia profesional", "licenseTierEnterprise": "Licencia Enterprise", - "licenseTierCommercial": "Licencia comercial", "licensed": "Licenciado", "yes": "Sí", "no": "Nu", @@ -1084,7 +1083,6 @@ "navbar": "Menú de navegación", "navbarDescription": "Menú de navegación principal para la aplicación", "navbarDocsLink": "Documentación", - "commercialEdition": "Edición Comercial", "otpErrorEnable": "No se puede habilitar 2FA", "otpErrorEnableDescription": "Se ha producido un error al habilitar 2FA", "otpSetupCheckCode": "Por favor, introduzca un código de 6 dígitos", diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 6028ab5b..4a1670f3 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Admin Serveur - Pangolin", "licenseTierProfessional": "Licence Professionnelle", "licenseTierEnterprise": "Licence Entreprise", - "licenseTierCommercial": "Licence commerciale", "licensed": "Sous licence", "yes": "Oui", "no": "Non", @@ -1084,7 +1083,6 @@ "navbar": "Menu de navigation", "navbarDescription": "Menu de navigation principal de l'application", "navbarDocsLink": "Documentation", - "commercialEdition": "Édition Commerciale", "otpErrorEnable": "Impossible d'activer l'A2F", "otpErrorEnableDescription": "Une erreur s'est produite lors de l'activation de l'A2F", "otpSetupCheckCode": "Veuillez entrer un code à 6 chiffres", diff --git a/messages/it-IT.json b/messages/it-IT.json index ca22ba63..143da0c5 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Server Admin - Pangolina", "licenseTierProfessional": "Licenza Professional", "licenseTierEnterprise": "Licenza Enterprise", - "licenseTierCommercial": "Licenza Commerciale", "licensed": "Con Licenza", "yes": "Sì", "no": "No", @@ -1084,7 +1083,6 @@ "navbar": "Menu di Navigazione", "navbarDescription": "Menu di navigazione principale dell'applicazione", "navbarDocsLink": "Documentazione", - "commercialEdition": "Edizione Commerciale", "otpErrorEnable": "Impossibile abilitare 2FA", "otpErrorEnableDescription": "Si è verificato un errore durante l'abilitazione di 2FA", "otpSetupCheckCode": "Inserisci un codice a 6 cifre", diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 3d010cd5..8ace81a5 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "서버 관리자 - 판골린", "licenseTierProfessional": "전문 라이센스", "licenseTierEnterprise": "기업 라이선스", - "licenseTierCommercial": "상업용 라이선스", "licensed": "라이센스", "yes": "예", "no": "아니요", @@ -1084,7 +1083,6 @@ "navbar": "탐색 메뉴", "navbarDescription": "애플리케이션의 주요 탐색 메뉴", "navbarDocsLink": "문서", - "commercialEdition": "상업용 에디션", "otpErrorEnable": "2FA를 활성화할 수 없습니다.", "otpErrorEnableDescription": "2FA를 활성화하는 동안 오류가 발생했습니다", "otpSetupCheckCode": "6자리 코드를 입력하세요", diff --git a/messages/nb-NO.json b/messages/nb-NO.json index 84dc5266..f7f0d3ab 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Server Admin - Pangolin", "licenseTierProfessional": "Profesjonell lisens", "licenseTierEnterprise": "Bedriftslisens", - "licenseTierCommercial": "Kommersiell lisens", "licensed": "Lisensiert", "yes": "Ja", "no": "Nei", @@ -1084,7 +1083,6 @@ "navbar": "Navigasjonsmeny", "navbarDescription": "Hovednavigasjonsmeny for applikasjonen", "navbarDocsLink": "Dokumentasjon", - "commercialEdition": "Kommersiell utgave", "otpErrorEnable": "Kunne ikke aktivere 2FA", "otpErrorEnableDescription": "En feil oppstod under aktivering av 2FA", "otpSetupCheckCode": "Vennligst skriv inn en 6-sifret kode", diff --git a/messages/nl-NL.json b/messages/nl-NL.json index fb82fbb6..34d1c811 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Serverbeheer - Pangolin", "licenseTierProfessional": "Professionele licentie", "licenseTierEnterprise": "Enterprise Licentie", - "licenseTierCommercial": "Commerciële licentie", "licensed": "Gelicentieerd", "yes": "ja", "no": "Neen", @@ -1084,7 +1083,6 @@ "navbar": "Navigatiemenu", "navbarDescription": "Hoofd navigatie menu voor de applicatie", "navbarDocsLink": "Documentatie", - "commercialEdition": "Commerciële editie", "otpErrorEnable": "Kan 2FA niet inschakelen", "otpErrorEnableDescription": "Er is een fout opgetreden tijdens het inschakelen van 2FA", "otpSetupCheckCode": "Voer een 6-cijferige code in", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index c3db35f5..08287ed9 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Administrator serwera - Pangolin", "licenseTierProfessional": "Licencja Professional", "licenseTierEnterprise": "Licencja Enterprise", - "licenseTierCommercial": "Licencja handlowa", "licensed": "Licencjonowany", "yes": "Tak", "no": "Nie", @@ -1084,7 +1083,6 @@ "navbar": "Menu nawigacyjne", "navbarDescription": "Główne menu nawigacyjne aplikacji", "navbarDocsLink": "Dokumentacja", - "commercialEdition": "Edycja komercyjna", "otpErrorEnable": "Nie można włączyć 2FA", "otpErrorEnableDescription": "Wystąpił błąd podczas włączania 2FA", "otpSetupCheckCode": "Wprowadź 6-cyfrowy kod", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1d61c581..5a45eedd 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Administrador do Servidor - Pangolin", "licenseTierProfessional": "Licença Profissional", "licenseTierEnterprise": "Licença Empresarial", - "licenseTierCommercial": "Licença comercial", "licensed": "Licenciado", "yes": "Sim", "no": "Não", @@ -1084,7 +1083,6 @@ "navbar": "Menu de Navegação", "navbarDescription": "Menu de navegação principal da aplicação", "navbarDocsLink": "Documentação", - "commercialEdition": "Edição Comercial", "otpErrorEnable": "Não foi possível ativar 2FA", "otpErrorEnableDescription": "Ocorreu um erro ao ativar 2FA", "otpSetupCheckCode": "Por favor, insira um código de 6 dígitos", diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 80be36dd..c41215d2 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Администратор сервера - Pangolin", "licenseTierProfessional": "Профессиональная лицензия", "licenseTierEnterprise": "Корпоративная лицензия", - "licenseTierCommercial": "Коммерческая лицензия", "licensed": "Лицензировано", "yes": "Да", "no": "Нет", @@ -1084,7 +1083,6 @@ "navbar": "Навигационное меню", "navbarDescription": "Главное навигационное меню приложения", "navbarDocsLink": "Документация", - "commercialEdition": "Коммерческая версия", "otpErrorEnable": "Невозможно включить 2FA", "otpErrorEnableDescription": "Произошла ошибка при включении 2FA", "otpSetupCheckCode": "Пожалуйста, введите 6-значный код", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index b84bef19..6296b7fe 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "Sunucu Yöneticisi - Pangolin", "licenseTierProfessional": "Profesyonel Lisans", "licenseTierEnterprise": "Kurumsal Lisans", - "licenseTierCommercial": "Ticari Lisans", "licensed": "Lisanslı", "yes": "Evet", "no": "Hayır", @@ -1084,7 +1083,6 @@ "navbar": "Navigasyon Menüsü", "navbarDescription": "Uygulamanın ana navigasyon menüsü", "navbarDocsLink": "Dokümantasyon", - "commercialEdition": "Ticari Sürüm", "otpErrorEnable": "2FA etkinleştirilemedi", "otpErrorEnableDescription": "2FA etkinleştirilirken bir hata oluştu", "otpSetupCheckCode": "6 haneli bir kod girin", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 4f1d9c14..a8f578db 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -715,7 +715,6 @@ "pangolinServerAdmin": "服务器管理员 - Pangolin", "licenseTierProfessional": "专业许可证", "licenseTierEnterprise": "企业许可证", - "licenseTierCommercial": "商业许可证", "licensed": "已授权", "yes": "是", "no": "否", @@ -1084,7 +1083,6 @@ "navbar": "导航菜单", "navbarDescription": "应用程序的主导航菜单", "navbarDocsLink": "文件", - "commercialEdition": "商业版", "otpErrorEnable": "无法启用 2FA", "otpErrorEnableDescription": "启用 2FA 时出错", "otpSetupCheckCode": "请输入您的6位数字代码", diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index e94bfe36..f7d45766 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -720,4 +720,4 @@ export type OrgDomains = InferSelectModel; export type SiteResource = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; -export type TargetHealthCheck = InferSelectModel; \ No newline at end of file +export type TargetHealthCheck = InferSelectModel; diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 9d64b85e..e3b0f7ed 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -759,4 +759,4 @@ export type SiteResource = InferSelectModel; export type OrgDomains = InferSelectModel; export type SetupToken = InferSelectModel; export type HostMeta = InferSelectModel; -export type TargetHealthCheck = InferSelectModel; \ No newline at end of file +export type TargetHealthCheck = InferSelectModel; diff --git a/server/lib/config.ts b/server/lib/config.ts index 103ea5ae..03b9e1ee 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -3,10 +3,11 @@ import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; import { db } from "@server/db"; import { SupporterKey, supporterKey } from "@server/db"; import { eq } from "drizzle-orm"; -import { license } from "@server/license/license"; +import { license } from "#dynamic/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; import { fromError } from "zod-validation-error"; import { build } from "@server/build"; +import logger from "@server/logger"; export class Config { private rawConfig!: z.infer; @@ -111,11 +112,11 @@ export class Config { } private async checkKeyStatus() { - const licenseStatus = await license.check(); - if ( - build != "oss" && - !licenseStatus.isHostLicensed - ) { + if (build === "enterprise") { + await license.check(); + } + + if (build == "oss") { this.checkSupporterKey(); } } diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index ea872252..af978c5d 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -12,39 +12,45 @@ const getEnvOrYaml = (envVar: string) => (valFromYaml: any) => { export const configSchema = z .object({ - app: z.object({ - dashboard_url: z - .string() - .url() - .pipe(z.string().url()) - .transform((url) => url.toLowerCase()) - .optional(), - log_level: z - .enum(["debug", "info", "warn", "error"]) - .optional() - .default("info"), - save_logs: z.boolean().optional().default(false), - log_failed_attempts: z.boolean().optional().default(false), - telemetry: z - .object({ - anonymous_usage: z.boolean().optional().default(true) - }) - .optional() - .default({}), - }).optional().default({ - log_level: "info", - save_logs: false, - log_failed_attempts: false, - telemetry: { - anonymous_usage: true - } - }), + app: z + .object({ + dashboard_url: z + .string() + .url() + .pipe(z.string().url()) + .transform((url) => url.toLowerCase()) + .optional(), + log_level: z + .enum(["debug", "info", "warn", "error"]) + .optional() + .default("info"), + save_logs: z.boolean().optional().default(false), + log_failed_attempts: z.boolean().optional().default(false), + telemetry: z + .object({ + anonymous_usage: z.boolean().optional().default(true) + }) + .optional() + .default({}) + }) + .optional() + .default({ + log_level: "info", + save_logs: false, + log_failed_attempts: false, + telemetry: { + anonymous_usage: true + } + }), managed: z .object({ name: z.string().optional(), id: z.string().optional(), secret: z.string().optional(), - endpoint: z.string().optional().default("https://pangolin.fossorial.io"), + endpoint: z + .string() + .optional() + .default("https://pangolin.fossorial.io"), redirect_endpoint: z.string().optional() }) .optional(), @@ -61,94 +67,95 @@ export const configSchema = z }) ) .optional(), - server: z.object({ - integration_port: portSchema - .optional() - .default(3003) - .transform(stoi) - .pipe(portSchema.optional()), - external_port: portSchema - .optional() - .default(3000) - .transform(stoi) - .pipe(portSchema), - internal_port: portSchema - .optional() - .default(3001) - .transform(stoi) - .pipe(portSchema), - next_port: portSchema - .optional() - .default(3002) - .transform(stoi) - .pipe(portSchema), - internal_hostname: z - .string() - .optional() - .default("pangolin") - .transform((url) => url.toLowerCase()), - session_cookie_name: z - .string() - .optional() - .default("p_session_token"), - resource_access_token_param: z - .string() - .optional() - .default("p_token"), - resource_access_token_headers: z - .object({ - id: z.string().optional().default("P-Access-Token-Id"), - token: z.string().optional().default("P-Access-Token") - }) - .optional() - .default({}), - resource_session_request_param: z - .string() - .optional() - .default("resource_session_request_param"), - dashboard_session_length_hours: z - .number() - .positive() - .gt(0) - .optional() - .default(720), - resource_session_length_hours: z - .number() - .positive() - .gt(0) - .optional() - .default(720), - cors: z - .object({ - origins: z.array(z.string()).optional(), - methods: z.array(z.string()).optional(), - allowed_headers: z.array(z.string()).optional(), - credentials: z.boolean().optional() - }) - .optional(), - trust_proxy: z.number().int().gte(0).optional().default(1), - secret: z - .string() - .pipe(z.string().min(8)) - .optional(), - maxmind_db_path: z.string().optional() - }).optional().default({ - integration_port: 3003, - external_port: 3000, - internal_port: 3001, - next_port: 3002, - internal_hostname: "pangolin", - session_cookie_name: "p_session_token", - resource_access_token_param: "p_token", - resource_access_token_headers: { - id: "P-Access-Token-Id", - token: "P-Access-Token" - }, - resource_session_request_param: "resource_session_request_param", - dashboard_session_length_hours: 720, - resource_session_length_hours: 720, - trust_proxy: 1 - }), + server: z + .object({ + integration_port: portSchema + .optional() + .default(3003) + .transform(stoi) + .pipe(portSchema.optional()), + external_port: portSchema + .optional() + .default(3000) + .transform(stoi) + .pipe(portSchema), + internal_port: portSchema + .optional() + .default(3001) + .transform(stoi) + .pipe(portSchema), + next_port: portSchema + .optional() + .default(3002) + .transform(stoi) + .pipe(portSchema), + internal_hostname: z + .string() + .optional() + .default("pangolin") + .transform((url) => url.toLowerCase()), + session_cookie_name: z + .string() + .optional() + .default("p_session_token"), + resource_access_token_param: z + .string() + .optional() + .default("p_token"), + resource_access_token_headers: z + .object({ + id: z.string().optional().default("P-Access-Token-Id"), + token: z.string().optional().default("P-Access-Token") + }) + .optional() + .default({}), + resource_session_request_param: z + .string() + .optional() + .default("resource_session_request_param"), + dashboard_session_length_hours: z + .number() + .positive() + .gt(0) + .optional() + .default(720), + resource_session_length_hours: z + .number() + .positive() + .gt(0) + .optional() + .default(720), + cors: z + .object({ + origins: z.array(z.string()).optional(), + methods: z.array(z.string()).optional(), + allowed_headers: z.array(z.string()).optional(), + credentials: z.boolean().optional() + }) + .optional(), + trust_proxy: z.number().int().gte(0).optional().default(1), + secret: z.string().pipe(z.string().min(8)).optional(), + maxmind_db_path: z.string().optional() + }) + .optional() + .default({ + integration_port: 3003, + external_port: 3000, + internal_port: 3001, + next_port: 3002, + internal_hostname: "pangolin", + session_cookie_name: "p_session_token", + resource_access_token_param: "p_token", + resource_access_token_headers: { + id: "P-Access-Token-Id", + token: "P-Access-Token" + }, + resource_session_request_param: + "resource_session_request_param", + dashboard_session_length_hours: 720, + resource_session_length_hours: 720, + trust_proxy: 1 + }), postgres: z .object({ connection_string: z.string().optional(), @@ -161,10 +168,26 @@ export const configSchema = z .optional(), pool: z .object({ - max_connections: z.number().positive().optional().default(20), - max_replica_connections: z.number().positive().optional().default(10), - idle_timeout_ms: z.number().positive().optional().default(30000), - connection_timeout_ms: z.number().positive().optional().default(5000) + max_connections: z + .number() + .positive() + .optional() + .default(20), + max_replica_connections: z + .number() + .positive() + .optional() + .default(10), + idle_timeout_ms: z + .number() + .positive() + .optional() + .default(30000), + connection_timeout_ms: z + .number() + .positive() + .optional() + .default(5000) }) .optional() .default({ @@ -193,7 +216,10 @@ export const configSchema = z .optional() .default("/var/dynamic/router_config.yml"), static_domains: z.array(z.string()).optional().default([]), - site_types: z.array(z.string()).optional().default(["newt", "wireguard", "local"]), + site_types: z + .array(z.string()) + .optional() + .default(["newt", "wireguard", "local"]), allow_raw_resources: z.boolean().optional().default(true), file_mode: z.boolean().optional().default(false) }) @@ -343,7 +369,10 @@ export const configSchema = z if (data.server?.secret === undefined) { data.server.secret = process.env.SERVER_SECRET; } - return data.server?.secret !== undefined && data.server.secret.length > 0; + return ( + data.server?.secret !== undefined && + data.server.secret.length > 0 + ); }, { message: "Server secret must be defined" @@ -356,7 +385,10 @@ export const configSchema = z return true; } // If hybrid is not defined, dashboard_url must be defined - return data.app.dashboard_url !== undefined && data.app.dashboard_url.length > 0; + return ( + data.app.dashboard_url !== undefined && + data.app.dashboard_url.length > 0 + ); }, { message: "Dashboard URL must be defined" diff --git a/server/lib/telemetry.ts b/server/lib/telemetry.ts index 827f1c7e..0e0ae24e 100644 --- a/server/lib/telemetry.ts +++ b/server/lib/telemetry.ts @@ -9,6 +9,7 @@ import { APP_VERSION } from "./consts"; import crypto from "crypto"; import { UserType } from "@server/types/UserTypes"; import { build } from "@server/build"; +import license from "@server/license/license"; class TelemetryClient { private client: PostHog | null = null; @@ -176,17 +177,36 @@ class TelemetryClient { const stats = await this.getSystemStats(); - this.client.capture({ - distinctId: hostMeta.hostMetaId, - event: "supporter_status", - properties: { - valid: stats.supporterStatus.valid, - tier: stats.supporterStatus.tier, - github_username: stats.supporterStatus.githubUsername - ? this.anon(stats.supporterStatus.githubUsername) - : "None" - } - }); + if (build === "enterprise") { + const licenseStatus = await license.check(); + const payload = { + distinctId: hostMeta.hostMetaId, + event: "enterprise_status", + properties: { + is_host_licensed: licenseStatus.isHostLicensed, + is_license_valid: licenseStatus.isLicenseValid, + license_tier: licenseStatus.tier || "unknown" + } + }; + logger.debug("Sending enterprise startup telemtry payload:", { + payload + }); + // this.client.capture(payload); + } + + if (build === "oss") { + this.client.capture({ + distinctId: hostMeta.hostMetaId, + event: "supporter_status", + properties: { + valid: stats.supporterStatus.valid, + tier: stats.supporterStatus.tier, + github_username: stats.supporterStatus.githubUsername + ? this.anon(stats.supporterStatus.githubUsername) + : "None" + } + }); + } this.client.capture({ distinctId: hostMeta.hostMetaId, diff --git a/server/license/license.ts b/server/license/license.ts index aeb628df..919fdb03 100644 --- a/server/license/license.ts +++ b/server/license/license.ts @@ -1,26 +1,17 @@ -import { db } from "@server/db"; -import { hostMeta, licenseKey, sites } from "@server/db"; -import logger from "@server/logger"; -import NodeCache from "node-cache"; -import { validateJWT } from "./licenseJwt"; -import { count, eq } from "drizzle-orm"; -import moment from "moment"; +import { db, hostMeta, HostMeta } from "@server/db"; import { setHostMeta } from "@server/lib/hostMeta"; -import { encrypt, decrypt } from "@server/lib/crypto"; -const keyTypes = ["HOST", "SITES"] as const; -type KeyType = (typeof keyTypes)[number]; +const keyTypes = ["host"] as const; +export type LicenseKeyType = (typeof keyTypes)[number]; -const keyTiers = ["PROFESSIONAL", "ENTERPRISE"] as const; -type KeyTier = (typeof keyTiers)[number]; +const keyTiers = ["personal", "enterprise"] as const; +export type LicenseKeyTier = (typeof keyTiers)[number]; export type LicenseStatus = { isHostLicensed: boolean; // Are there any license keys? isLicenseValid: boolean; // Is the license key valid? hostId: string; // Host ID - maxSites?: number; - usedSites?: number; - tier?: KeyTier; + tier?: LicenseKeyTier; }; export type LicenseKeyCache = { @@ -28,451 +19,27 @@ export type LicenseKeyCache = { licenseKeyEncrypted: string; valid: boolean; iat?: Date; - type?: KeyType; - tier?: KeyTier; - numSites?: number; -}; - -type ActivateLicenseKeyAPIResponse = { - data: { - instanceId: string; - }; - success: boolean; - error: string; - message: string; - status: number; -}; - -type ValidateLicenseAPIResponse = { - data: { - licenseKeys: { - [key: string]: string; - }; - }; - success: boolean; - error: string; - message: string; - status: number; -}; - -type TokenPayload = { - valid: boolean; - type: KeyType; - tier: KeyTier; - quantity: number; - terminateAt: string; // ISO - iat: number; // Issued at + type?: LicenseKeyType; + tier?: LicenseKeyTier; + terminateAt?: Date; }; export class License { - private phoneHomeInterval = 6 * 60 * 60; // 6 hours = 6 * 60 * 60 = 21600 seconds - private validationServerUrl = - "https://api.fossorial.io/api/v1/license/professional/validate"; - private activationServerUrl = - "https://api.fossorial.io/api/v1/license/professional/activate"; - - private statusCache = new NodeCache({ stdTTL: this.phoneHomeInterval }); - private licenseKeyCache = new NodeCache(); - - private ephemeralKey!: string; - private statusKey = "status"; private serverSecret!: string; - private publicKey = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx9RKc8cw+G8r7h/xeozF -FNkRDggQfYO6Ae+EWHGujZ9WYAZ10spLh9F/zoLhhr3XhsjpoRXwMfgNuO5HstWf -CYM20I0l7EUUMWEyWd4tZLd+5XQ4jY5xWOCWyFJAGQSp7flcRmxdfde+l+xg9eKl -apbY84aVp09/GqM96hCS+CsQZrhohu/aOqYVB/eAhF01qsbmiZ7Y3WtdhTldveYt -h4mZWGmjf8d/aEgePf/tk1gp0BUxf+Ae5yqoAqU+6aiFbjJ7q1kgxc18PWFGfE9y -zSk+OZk887N5ThQ52154+oOUCMMR2Y3t5OH1hVZod51vuY2u5LsQXsf+87PwB91y -LQIDAQAB ------END PUBLIC KEY-----`; + constructor(private hostMeta: HostMeta) {} - constructor(private hostId: string) { - this.ephemeralKey = Buffer.from( - JSON.stringify({ ts: new Date().toISOString() }) - ).toString("base64"); - - setInterval( - async () => { - await this.check(); - }, - 1000 * 60 * 60 - ); // 1 hour = 60 * 60 = 3600 seconds - } - - public listKeys(): LicenseKeyCache[] { - const keys = this.licenseKeyCache.keys(); - return keys.map((key) => { - return this.licenseKeyCache.get(key)!; - }); + public async check(): Promise { + return { + hostId: this.hostMeta.hostMetaId, + isHostLicensed: false, + isLicenseValid: false + }; } public setServerSecret(secret: string) { this.serverSecret = secret; } - - public async forceRecheck() { - this.statusCache.flushAll(); - this.licenseKeyCache.flushAll(); - - return await this.check(); - } - - public async isUnlocked(): Promise { - const status = await this.check(); - if (status.isHostLicensed) { - if (status.isLicenseValid) { - return true; - } - } - return false; - } - - public async check(): Promise { - // Set used sites - const [siteCount] = await db - .select({ - value: count() - }) - .from(sites); - - const status: LicenseStatus = { - hostId: this.hostId, - isHostLicensed: true, - isLicenseValid: false, - maxSites: undefined, - usedSites: siteCount.value - }; - - try { - if (this.statusCache.has(this.statusKey)) { - const res = this.statusCache.get("status") as LicenseStatus; - res.usedSites = status.usedSites; - return res; - } - - // Invalidate all - this.licenseKeyCache.flushAll(); - - const allKeysRes = await db.select().from(licenseKey); - - if (allKeysRes.length === 0) { - status.isHostLicensed = false; - return status; - } - - let foundHostKey = false; - // Validate stored license keys - for (const key of allKeysRes) { - try { - // Decrypt the license key and token - const decryptedKey = decrypt( - key.licenseKeyId, - this.serverSecret - ); - const decryptedToken = decrypt( - key.token, - this.serverSecret - ); - - const payload = validateJWT( - decryptedToken, - this.publicKey - ); - - this.licenseKeyCache.set(decryptedKey, { - licenseKey: decryptedKey, - licenseKeyEncrypted: key.licenseKeyId, - valid: payload.valid, - type: payload.type, - tier: payload.tier, - numSites: payload.quantity, - iat: new Date(payload.iat * 1000) - }); - - if (payload.type === "HOST") { - foundHostKey = true; - } - } catch (e) { - logger.error( - `Error validating license key: ${key.licenseKeyId}` - ); - logger.error(e); - - this.licenseKeyCache.set( - key.licenseKeyId, - { - licenseKey: key.licenseKeyId, - licenseKeyEncrypted: key.licenseKeyId, - valid: false - } - ); - } - } - - if (!foundHostKey && allKeysRes.length) { - logger.debug("No host license key found"); - status.isHostLicensed = false; - } - - const keys = allKeysRes.map((key) => ({ - licenseKey: decrypt(key.licenseKeyId, this.serverSecret), - instanceId: decrypt(key.instanceId, this.serverSecret) - })); - - let apiResponse: ValidateLicenseAPIResponse | undefined; - try { - // Phone home to validate license keys - apiResponse = await this.phoneHome(keys); - - if (!apiResponse?.success) { - throw new Error(apiResponse?.error); - } - } catch (e) { - logger.error("Error communicating with license server:"); - logger.error(e); - } - - logger.debug("Validate response", apiResponse); - - // Check and update all license keys with server response - for (const key of keys) { - try { - const cached = this.licenseKeyCache.get( - key.licenseKey - )!; - const licenseKeyRes = - apiResponse?.data?.licenseKeys[key.licenseKey]; - - if (!apiResponse || !licenseKeyRes) { - logger.debug( - `No response from server for license key: ${key.licenseKey}` - ); - if (cached.iat) { - const exp = moment(cached.iat) - .add(7, "days") - .toDate(); - if (exp > new Date()) { - logger.debug( - `Using cached license key: ${key.licenseKey}, valid ${cached.valid}` - ); - continue; - } - } - - logger.debug( - `Can't trust license key: ${key.licenseKey}` - ); - cached.valid = false; - this.licenseKeyCache.set( - key.licenseKey, - cached - ); - continue; - } - - const payload = validateJWT( - licenseKeyRes, - this.publicKey - ); - cached.valid = payload.valid; - cached.type = payload.type; - cached.tier = payload.tier; - cached.numSites = payload.quantity; - cached.iat = new Date(payload.iat * 1000); - - // Encrypt the updated token before storing - const encryptedKey = encrypt( - key.licenseKey, - this.serverSecret - ); - const encryptedToken = encrypt( - licenseKeyRes, - this.serverSecret - ); - - await db - .update(licenseKey) - .set({ - token: encryptedToken - }) - .where(eq(licenseKey.licenseKeyId, encryptedKey)); - - this.licenseKeyCache.set( - key.licenseKey, - cached - ); - } catch (e) { - logger.error(`Error validating license key: ${key}`); - logger.error(e); - } - } - - // Compute host status - for (const key of keys) { - const cached = this.licenseKeyCache.get( - key.licenseKey - )!; - - logger.debug("Checking key", cached); - - if (cached.type === "HOST") { - status.isLicenseValid = cached.valid; - status.tier = cached.tier; - } - - if (!cached.valid) { - continue; - } - - if (!status.maxSites) { - status.maxSites = 0; - } - - status.maxSites += cached.numSites || 0; - } - } catch (error) { - logger.error("Error checking license status:"); - logger.error(error); - } - - this.statusCache.set(this.statusKey, status); - return status; - } - - public async activateLicenseKey(key: string) { - // Encrypt the license key before storing - const encryptedKey = encrypt(key, this.serverSecret); - - const [existingKey] = await db - .select() - .from(licenseKey) - .where(eq(licenseKey.licenseKeyId, encryptedKey)) - .limit(1); - - if (existingKey) { - throw new Error("License key already exists"); - } - - let instanceId: string | undefined; - try { - // Call activate - const apiResponse = await fetch(this.activationServerUrl, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - licenseKey: key, - instanceName: this.hostId - }) - }); - - const data = await apiResponse.json(); - - if (!data.success) { - throw new Error(`${data.message || data.error}`); - } - - const response = data as ActivateLicenseKeyAPIResponse; - - if (!response.data) { - throw new Error("No response from server"); - } - - if (!response.data.instanceId) { - throw new Error("No instance ID in response"); - } - - instanceId = response.data.instanceId; - } catch (error) { - throw Error(`Error activating license key: ${error}`); - } - - // Phone home to validate license key - const keys = [ - { - licenseKey: key, - instanceId: instanceId! - } - ]; - - let validateResponse: ValidateLicenseAPIResponse; - try { - validateResponse = await this.phoneHome(keys); - - if (!validateResponse) { - throw new Error("No response from server"); - } - - if (!validateResponse.success) { - throw new Error(validateResponse.error); - } - - // Validate the license key - const licenseKeyRes = validateResponse.data.licenseKeys[key]; - if (!licenseKeyRes) { - throw new Error("Invalid license key"); - } - - const payload = validateJWT( - licenseKeyRes, - this.publicKey - ); - - if (!payload.valid) { - throw new Error("Invalid license key"); - } - - const encryptedToken = encrypt(licenseKeyRes, this.serverSecret); - // Encrypt the instanceId before storing - const encryptedInstanceId = encrypt(instanceId!, this.serverSecret); - - // Store the license key in the database - await db.insert(licenseKey).values({ - licenseKeyId: encryptedKey, - token: encryptedToken, - instanceId: encryptedInstanceId - }); - } catch (error) { - throw Error(`Error validating license key: ${error}`); - } - - // Invalidate the cache and re-compute the status - return await this.forceRecheck(); - } - - private async phoneHome( - keys: { - licenseKey: string; - instanceId: string; - }[] - ): Promise { - // Decrypt the instanceIds before sending to the server - const decryptedKeys = keys.map((key) => ({ - licenseKey: key.licenseKey, - instanceId: key.instanceId - ? decrypt(key.instanceId, this.serverSecret) - : key.instanceId - })); - - const response = await fetch(this.validationServerUrl, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - licenseKeys: decryptedKeys, - ephemeralKey: this.ephemeralKey, - instanceName: this.hostId - }) - }); - - const data = await response.json(); - - return data as ValidateLicenseAPIResponse; - } } await setHostMeta(); @@ -483,6 +50,6 @@ if (!info) { throw new Error("Host information not found"); } -export const license = new License(info.hostMetaId); +export const license = new License(info); export default license; diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts index f211fa9e..629cafe9 100644 --- a/server/middlewares/index.ts +++ b/server/middlewares/index.ts @@ -21,10 +21,9 @@ export * from "./verifyIsLoggedInUser"; export * from "./verifyIsLoggedInUser"; export * from "./verifyClientAccess"; export * from "./integration"; -export * from "./verifyValidLicense"; export * from "./verifyUserHasAction"; export * from "./verifyApiKeyAccess"; export * from "./verifyDomainAccess"; export * from "./verifyClientsEnabled"; export * from "./verifyUserIsOrgOwner"; -export * from "./verifySiteResourceAccess"; \ No newline at end of file +export * from "./verifySiteResourceAccess"; diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts index a0dbf9a3..c6ecdde7 100644 --- a/server/private/lib/config.ts +++ b/server/private/lib/config.ts @@ -12,11 +12,8 @@ */ import { z } from "zod"; -import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; -import { db } from "@server/db"; -import { SupporterKey, supporterKey } from "@server/db"; -import { eq } from "drizzle-orm"; -import { license } from "@server/license/license"; +import { __DIRNAME } from "@server/lib/consts"; +import { SupporterKey } from "@server/db"; import { fromError } from "zod-validation-error"; import { privateConfigSchema, @@ -143,7 +140,8 @@ export class PrivateConfig { process.env.S3_BUCKET = parsedPrivateConfig.stripe.s3Bucket; } if (parsedPrivateConfig.stripe?.localFilePath) { - process.env.LOCAL_FILE_PATH = parsedPrivateConfig.stripe.localFilePath; + process.env.LOCAL_FILE_PATH = + parsedPrivateConfig.stripe.localFilePath; } if (parsedPrivateConfig.stripe?.s3Region) { process.env.S3_REGION = parsedPrivateConfig.stripe.s3Region; diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts index c1847ba5..c2af299c 100644 --- a/server/private/lib/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -36,6 +36,7 @@ export const privateConfigSchema = z .pipe(z.string().min(8)), resend_api_key: z.string().optional(), reo_client_id: z.string().optional(), + fossorial_api_key: z.string().optional() }).optional().default({ encryption_key_path: "./config/encryption.pem" }), @@ -164,6 +165,9 @@ export function readPrivateConfigFile() { const loadConfig = (configPath: string) => { try { const yamlContent = fs.readFileSync(configPath, "utf8"); + if (yamlContent.trim() === "") { + return {}; + } const config = yaml.load(yamlContent); return config; } catch (error) { @@ -176,7 +180,7 @@ export function readPrivateConfigFile() { } }; - let environment: any; + let environment: any = {}; if (fs.existsSync(privateConfigFilePath1)) { environment = loadConfig(privateConfigFilePath1); } diff --git a/server/private/license/license.ts b/server/private/license/license.ts new file mode 100644 index 00000000..d1138539 --- /dev/null +++ b/server/private/license/license.ts @@ -0,0 +1,459 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { db, HostMeta } from "@server/db"; +import { hostMeta, licenseKey } from "@server/db"; +import logger from "@server/logger"; +import NodeCache from "node-cache"; +import { validateJWT } from "./licenseJwt"; +import { eq } from "drizzle-orm"; +import moment from "moment"; +import { encrypt, decrypt } from "@server/lib/crypto"; +import { + LicenseKeyCache, + LicenseKeyTier, + LicenseKeyType, + LicenseStatus +} from "@server/license/license"; +import { setHostMeta } from "@server/lib/hostMeta"; + +type ActivateLicenseKeyAPIResponse = { + data: { + instanceId: string; + }; + success: boolean; + error: string; + message: string; + status: number; +}; + +type ValidateLicenseAPIResponse = { + data: { + licenseKeys: { + [key: string]: string; + }; + }; + success: boolean; + error: string; + message: string; + status: number; +}; + +type TokenPayload = { + valid: boolean; + type: LicenseKeyType; + tier: LicenseKeyTier; + quantity: number; + terminateAt: string; // ISO + iat: number; // Issued at +}; + +export class License { + private phoneHomeInterval = 6 * 60 * 60; // 6 hours = 6 * 60 * 60 = 21600 seconds + private serverBaseUrl = "https://api.fossorial.io"; + private validationServerUrl = `${this.serverBaseUrl}/api/v1/license/enterprise/validate`; + private activationServerUrl = `${this.serverBaseUrl}/api/v1/license/enterprise/activate`; + + private statusCache = new NodeCache({ stdTTL: this.phoneHomeInterval }); + private licenseKeyCache = new NodeCache(); + + private statusKey = "status"; + private serverSecret!: string; + + private publicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx9RKc8cw+G8r7h/xeozF +FNkRDggQfYO6Ae+EWHGujZ9WYAZ10spLh9F/zoLhhr3XhsjpoRXwMfgNuO5HstWf +CYM20I0l7EUUMWEyWd4tZLd+5XQ4jY5xWOCWyFJAGQSp7flcRmxdfde+l+xg9eKl +apbY84aVp09/GqM96hCS+CsQZrhohu/aOqYVB/eAhF01qsbmiZ7Y3WtdhTldveYt +h4mZWGmjf8d/aEgePf/tk1gp0BUxf+Ae5yqoAqU+6aiFbjJ7q1kgxc18PWFGfE9y +zSk+OZk887N5ThQ52154+oOUCMMR2Y3t5OH1hVZod51vuY2u5LsQXsf+87PwB91y +LQIDAQAB +-----END PUBLIC KEY-----`; + + constructor(private hostMeta: HostMeta) { + setInterval( + async () => { + await this.check(); + }, + 1000 * 60 * 60 + ); + } + + public listKeys(): LicenseKeyCache[] { + const keys = this.licenseKeyCache.keys(); + return keys.map((key) => { + return this.licenseKeyCache.get(key)!; + }); + } + + public setServerSecret(secret: string) { + this.serverSecret = secret; + } + + public async forceRecheck() { + this.statusCache.flushAll(); + this.licenseKeyCache.flushAll(); + + return await this.check(); + } + + public async isUnlocked(): Promise { + const status = await this.check(); + if (status.isHostLicensed) { + if (status.isLicenseValid) { + return true; + } + } + return false; + } + + public async check(): Promise { + const status: LicenseStatus = { + hostId: this.hostMeta.hostMetaId, + isHostLicensed: true, + isLicenseValid: false + }; + + try { + if (this.statusCache.has(this.statusKey)) { + const res = this.statusCache.get("status") as LicenseStatus; + return res; + } + // Invalidate all + this.licenseKeyCache.flushAll(); + + const allKeysRes = await db.select().from(licenseKey); + + if (allKeysRes.length === 0) { + status.isHostLicensed = false; + return status; + } + + let foundHostKey = false; + // Validate stored license keys + for (const key of allKeysRes) { + try { + // Decrypt the license key and token + const decryptedKey = decrypt( + key.licenseKeyId, + this.serverSecret + ); + const decryptedToken = decrypt( + key.token, + this.serverSecret + ); + + const payload = validateJWT( + decryptedToken, + this.publicKey + ); + + this.licenseKeyCache.set(decryptedKey, { + licenseKey: decryptedKey, + licenseKeyEncrypted: key.licenseKeyId, + valid: payload.valid, + type: payload.type, + tier: payload.tier, + iat: new Date(payload.iat * 1000), + terminateAt: new Date(payload.terminateAt) + }); + + if (payload.type === "host") { + foundHostKey = true; + } + } catch (e) { + logger.error( + `Error validating license key: ${key.licenseKeyId}` + ); + logger.error(e); + + this.licenseKeyCache.set( + key.licenseKeyId, + { + licenseKey: key.licenseKeyId, + licenseKeyEncrypted: key.licenseKeyId, + valid: false + } + ); + } + } + + if (!foundHostKey && allKeysRes.length) { + logger.debug("No host license key found"); + status.isHostLicensed = false; + } + + const keys = allKeysRes.map((key) => ({ + licenseKey: decrypt(key.licenseKeyId, this.serverSecret), + instanceId: decrypt(key.instanceId, this.serverSecret) + })); + + let apiResponse: ValidateLicenseAPIResponse | undefined; + try { + // Phone home to validate license keys + apiResponse = await this.phoneHome(keys, false); + + if (!apiResponse?.success) { + throw new Error(apiResponse?.error); + } + } catch (e) { + logger.error("Error communicating with license server:"); + logger.error(e); + } + + // Check and update all license keys with server response + for (const key of keys) { + try { + const cached = this.licenseKeyCache.get( + key.licenseKey + )!; + const licenseKeyRes = + apiResponse?.data?.licenseKeys[key.licenseKey]; + + if (!apiResponse || !licenseKeyRes) { + logger.debug( + `No response from server for license key: ${key.licenseKey}` + ); + if (cached.iat) { + const exp = moment(cached.iat) + .add(7, "days") + .toDate(); + if (exp > new Date()) { + logger.debug( + `Using cached license key: ${key.licenseKey}, valid ${cached.valid}` + ); + continue; + } + } + + logger.debug( + `Can't trust license key: ${key.licenseKey}` + ); + cached.valid = false; + this.licenseKeyCache.set( + key.licenseKey, + cached + ); + continue; + } + + const payload = validateJWT( + licenseKeyRes, + this.publicKey + ); + cached.valid = payload.valid; + cached.type = payload.type; + cached.tier = payload.tier; + cached.iat = new Date(payload.iat * 1000); + + // Encrypt the updated token before storing + const encryptedKey = encrypt( + key.licenseKey, + this.serverSecret + ); + const encryptedToken = encrypt( + licenseKeyRes, + this.serverSecret + ); + + await db + .update(licenseKey) + .set({ + token: encryptedToken + }) + .where(eq(licenseKey.licenseKeyId, encryptedKey)); + + this.licenseKeyCache.set( + key.licenseKey, + cached + ); + } catch (e) { + logger.error(`Error validating license key: ${key}`); + logger.error(e); + } + } + + // Compute host status + for (const key of keys) { + const cached = this.licenseKeyCache.get( + key.licenseKey + )!; + + if (cached.type === "host") { + status.isLicenseValid = cached.valid; + status.tier = cached.tier; + } + + if (!cached.valid) { + continue; + } + } + } catch (error) { + logger.error("Error checking license status:"); + logger.error(error); + } + + this.statusCache.set(this.statusKey, status); + return status; + } + + public async activateLicenseKey(key: string) { + // Encrypt the license key before storing + const encryptedKey = encrypt(key, this.serverSecret); + + const [existingKey] = await db + .select() + .from(licenseKey) + .where(eq(licenseKey.licenseKeyId, encryptedKey)) + .limit(1); + + if (existingKey) { + throw new Error("License key already exists"); + } + + let instanceId: string | undefined; + try { + // Call activate + const apiResponse = await fetch(this.activationServerUrl, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + licenseKey: key, + instanceName: this.hostMeta.hostMetaId + }) + }); + + const data = await apiResponse.json(); + + if (!data.success) { + throw new Error(`${data.message || data.error}`); + } + + const response = data as ActivateLicenseKeyAPIResponse; + + if (!response.data) { + throw new Error("No response from server"); + } + + if (!response.data.instanceId) { + throw new Error("No instance ID in response"); + } + + logger.debug("Activated license key, instance ID:", { + instanceId: response.data.instanceId + }); + + instanceId = response.data.instanceId; + } catch (error) { + throw Error(`Error activating license key: ${error}`); + } + + // Phone home to validate license key + const keys = [ + { + licenseKey: key, + instanceId: instanceId! + } + ]; + + let validateResponse: ValidateLicenseAPIResponse; + try { + validateResponse = await this.phoneHome(keys, false); + + if (!validateResponse) { + throw new Error("No response from server"); + } + + if (!validateResponse.success) { + throw new Error(validateResponse.error); + } + + // Validate the license key + const licenseKeyRes = validateResponse.data.licenseKeys[key]; + if (!licenseKeyRes) { + throw new Error("Invalid license key"); + } + + const payload = validateJWT( + licenseKeyRes, + this.publicKey + ); + + if (!payload.valid) { + throw new Error("Invalid license key"); + } + + const encryptedToken = encrypt(licenseKeyRes, this.serverSecret); + // Encrypt the instanceId before storing + const encryptedInstanceId = encrypt(instanceId!, this.serverSecret); + + // Store the license key in the database + await db.insert(licenseKey).values({ + licenseKeyId: encryptedKey, + token: encryptedToken, + instanceId: encryptedInstanceId + }); + } catch (error) { + throw Error(`Error validating license key: ${error}`); + } + + // Invalidate the cache and re-compute the status + return await this.forceRecheck(); + } + + private async phoneHome( + keys: { + licenseKey: string; + instanceId: string; + }[], + doDecrypt = true + ): Promise { + // Decrypt the instanceIds before sending to the server + const decryptedKeys = keys.map((key) => ({ + licenseKey: key.licenseKey, + instanceId: + key.instanceId && doDecrypt + ? decrypt(key.instanceId, this.serverSecret) + : key.instanceId + })); + + const response = await fetch(this.validationServerUrl, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + licenseKeys: decryptedKeys, + instanceName: this.hostMeta.hostMetaId + }) + }); + + const data = await response.json(); + + return data as ValidateLicenseAPIResponse; + } +} + +await setHostMeta(); + +const [info] = await db.select().from(hostMeta).limit(1); + +if (!info) { + throw new Error("Host information not found"); +} + +export const license = new License(info); + +export default license; diff --git a/server/license/licenseJwt.ts b/server/private/license/licenseJwt.ts similarity index 88% rename from server/license/licenseJwt.ts rename to server/private/license/licenseJwt.ts index 3d148e51..f137db30 100644 --- a/server/license/licenseJwt.ts +++ b/server/private/license/licenseJwt.ts @@ -1,3 +1,16 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + import * as crypto from "crypto"; /** diff --git a/server/middlewares/verifyValidLicense.ts b/server/private/middlewares/verifyValidLicense.ts similarity index 81% rename from server/middlewares/verifyValidLicense.ts rename to server/private/middlewares/verifyValidLicense.ts index 7e3bfee3..b265fcbd 100644 --- a/server/middlewares/verifyValidLicense.ts +++ b/server/private/middlewares/verifyValidLicense.ts @@ -1,7 +1,8 @@ import { Request, Response, NextFunction } from "express"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import license from "@server/license/license"; +import license from "#private/license/license"; +import { build } from "@server/build"; export async function verifyValidLicense( req: Request, @@ -9,6 +10,10 @@ export async function verifyValidLicense( next: NextFunction ) { try { + if (build !== "saas") { + return next(); + } + const unlocked = await license.isUnlocked(); if (!unlocked) { return next( diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index fac7c0c4..c91943f6 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -19,9 +19,16 @@ import * as loginPage from "#private/routers/loginPage"; import * as orgIdp from "#private/routers/orgIdp"; import * as domain from "#private/routers/domain"; import * as auth from "#private/routers/auth"; +import * as license from "#private/routers/license"; +import * as generateLicense from "./generatedLicense"; import { Router } from "express"; -import { verifyOrgAccess, verifySessionUserMiddleware, verifyUserHasAction } from "@server/middlewares"; +import { + verifyOrgAccess, + verifyUserHasAction, + verifyUserIsOrgOwner, + verifyUserIsServerAdmin +} from "@server/middlewares"; import { ActionsEnum } from "@server/auth/actions"; import { verifyCertificateAccess, @@ -33,28 +40,19 @@ import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { unauthenticated as ua, authenticated as a } from "@server/routers/external"; +import { + unauthenticated as ua, + authenticated as a +} from "@server/routers/external"; +import { verifyValidLicense } from "../middlewares/verifyValidLicense"; +import { build } from "@server/build"; export const authenticated = a; export const unauthenticated = ua; -unauthenticated.post( - "/quick-start", - rateLimit({ - windowMs: 15 * 60 * 1000, - max: 100, - keyGenerator: (req) => req.path, - handler: (req, res, next) => { - const message = `We're too busy right now. Please try again later.`; - return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); - }, - store: createStore() - }), - auth.quickStart -); - unauthenticated.post( "/remote-exit-node/quick-start", + verifyValidLicense, rateLimit({ windowMs: 60 * 60 * 1000, max: 5, @@ -68,9 +66,9 @@ unauthenticated.post( remoteExitNode.quickStartRemoteExitNode ); - authenticated.put( "/org/:orgId/idp/oidc", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.createIdp), orgIdp.createOrgOidcIdp @@ -78,6 +76,7 @@ authenticated.put( authenticated.post( "/org/:orgId/idp/:idpId/oidc", + verifyValidLicense, verifyOrgAccess, verifyIdpAccess, verifyUserHasAction(ActionsEnum.updateIdp), @@ -86,6 +85,7 @@ authenticated.post( authenticated.delete( "/org/:orgId/idp/:idpId", + verifyValidLicense, verifyOrgAccess, verifyIdpAccess, verifyUserHasAction(ActionsEnum.deleteIdp), @@ -94,6 +94,7 @@ authenticated.delete( authenticated.get( "/org/:orgId/idp/:idpId", + verifyValidLicense, verifyOrgAccess, verifyIdpAccess, verifyUserHasAction(ActionsEnum.getIdp), @@ -102,6 +103,7 @@ authenticated.get( authenticated.get( "/org/:orgId/idp", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.listIdps), orgIdp.listOrgIdps @@ -111,6 +113,7 @@ authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this authenticated.get( "/org/:orgId/certificate/:domainId/:domain", + verifyValidLicense, verifyOrgAccess, verifyCertificateAccess, verifyUserHasAction(ActionsEnum.getCertificate), @@ -119,49 +122,87 @@ authenticated.get( authenticated.post( "/org/:orgId/certificate/:certId/restart", + verifyValidLicense, verifyOrgAccess, verifyCertificateAccess, verifyUserHasAction(ActionsEnum.restartCertificate), certificates.restartCertificate ); -authenticated.post( - "/org/:orgId/billing/create-checkout-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createCheckoutSession -); +if (build === "saas") { + unauthenticated.post( + "/quick-start", + rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + keyGenerator: (req) => req.path, + handler: (req, res, next) => { + const message = `We're too busy right now. Please try again later.`; + return next( + createHttpError(HttpCode.TOO_MANY_REQUESTS, message) + ); + }, + store: createStore() + }), + auth.quickStart + ); -authenticated.post( - "/org/:orgId/billing/create-portal-session", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.createPortalSession -); + authenticated.post( + "/org/:orgId/billing/create-checkout-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createCheckoutSession + ); + + authenticated.post( + "/org/:orgId/billing/create-portal-session", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.createPortalSession + ); + + authenticated.get( + "/org/:orgId/billing/subscription", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgSubscription + ); + + authenticated.get( + "/org/:orgId/billing/usage", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.billing), + billing.getOrgUsage + ); + + authenticated.get( + "/org/:orgId/license", + verifyOrgAccess, + generateLicense.listSaasLicenseKeys + ); + + authenticated.put( + "/org/:orgId/license", + verifyOrgAccess, + generateLicense.generateNewLicense + ); +} authenticated.get( - "/org/:orgId/billing/subscription", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgSubscription + "/domain/namespaces", + verifyValidLicense, + domain.listDomainNamespaces ); -authenticated.get( - "/org/:orgId/billing/usage", - verifyOrgAccess, - verifyUserHasAction(ActionsEnum.billing), - billing.getOrgUsage -); - -authenticated.get("/domain/namespaces", domain.listDomainNamespaces); - authenticated.get( "/domain/check-namespace-availability", + verifyValidLicense, domain.checkDomainNamespaceAvailability ); authenticated.put( "/org/:orgId/remote-exit-node", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.createRemoteExitNode), remoteExitNode.createRemoteExitNode @@ -169,6 +210,7 @@ authenticated.put( authenticated.get( "/org/:orgId/remote-exit-nodes", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.listRemoteExitNode), remoteExitNode.listRemoteExitNodes @@ -176,6 +218,7 @@ authenticated.get( authenticated.get( "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyValidLicense, verifyOrgAccess, verifyRemoteExitNodeAccess, verifyUserHasAction(ActionsEnum.getRemoteExitNode), @@ -184,6 +227,7 @@ authenticated.get( authenticated.get( "/org/:orgId/pick-remote-exit-node-defaults", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.createRemoteExitNode), remoteExitNode.pickRemoteExitNodeDefaults @@ -191,6 +235,7 @@ authenticated.get( authenticated.delete( "/org/:orgId/remote-exit-node/:remoteExitNodeId", + verifyValidLicense, verifyOrgAccess, verifyRemoteExitNodeAccess, verifyUserHasAction(ActionsEnum.deleteRemoteExitNode), @@ -199,6 +244,7 @@ authenticated.delete( authenticated.put( "/org/:orgId/login-page", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.createLoginPage), loginPage.createLoginPage @@ -206,6 +252,7 @@ authenticated.put( authenticated.post( "/org/:orgId/login-page/:loginPageId", + verifyValidLicense, verifyOrgAccess, verifyLoginPageAccess, verifyUserHasAction(ActionsEnum.updateLoginPage), @@ -214,6 +261,7 @@ authenticated.post( authenticated.delete( "/org/:orgId/login-page/:loginPageId", + verifyValidLicense, verifyOrgAccess, verifyLoginPageAccess, verifyUserHasAction(ActionsEnum.deleteLoginPage), @@ -222,6 +270,7 @@ authenticated.delete( authenticated.get( "/org/:orgId/login-page", + verifyValidLicense, verifyOrgAccess, verifyUserHasAction(ActionsEnum.getLoginPage), loginPage.getLoginPage @@ -231,6 +280,7 @@ export const authRouter = Router(); authRouter.post( "/remoteExitNode/get-token", + verifyValidLicense, rateLimit({ windowMs: 15 * 60 * 1000, max: 900, @@ -247,6 +297,7 @@ authRouter.post( authRouter.post( "/transfer-session-token", + verifyValidLicense, rateLimit({ windowMs: 1 * 60 * 1000, max: 60, @@ -259,4 +310,28 @@ authRouter.post( store: createStore() }), auth.transferSession -); \ No newline at end of file +); + +authenticated.post( + "/license/activate", + verifyUserIsServerAdmin, + license.activateLicense +); + +authenticated.get( + "/license/keys", + verifyUserIsServerAdmin, + license.listLicenseKeys +); + +authenticated.delete( + "/license/:licenseKey", + verifyUserIsServerAdmin, + license.deleteLicenseKey +); + +authenticated.post( + "/license/recheck", + verifyUserIsServerAdmin, + license.recheckStatus +); diff --git a/server/private/routers/generatedLicense/generateNewLicense.ts b/server/private/routers/generatedLicense/generateNewLicense.ts new file mode 100644 index 00000000..cb179a2d --- /dev/null +++ b/server/private/routers/generatedLicense/generateNewLicense.ts @@ -0,0 +1,91 @@ +import { Request, Response, NextFunction } from "express"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { response as sendResponse } from "@server/lib/response"; +import privateConfig from "@server/private/lib/config"; + +export type NewLicenseKey = { + licenseKey: { + id: number; + instanceName: string | null; + instanceId: string; + licenseKey: string; + tier: string; + type: string; + quantity: number; + isValid: boolean; + updatedAt: string; + createdAt: string; + expiresAt: string; + orgId: string; + }; +}; + +export type GenerateNewLicenseResponse = NewLicenseKey; + +async function createNewLicense(orgId: string, licenseData: any): Promise { + try { + const response = await fetch( + `https://api.fossorial.io/api/v1/license-internal/enterprise/${orgId}/create`, + { + method: "PUT", + headers: { + "api-key": + privateConfig.getRawPrivateConfig().server + .fossorial_api_key!, + "Content-Type": "application/json" + }, + body: JSON.stringify(licenseData) + } + ); + + const data = await response.json(); + + logger.debug("Fossorial API response:", {data}); + return data; + } catch (error) { + console.error("Error creating new license:", error); + throw error; + } +} + +export async function generateNewLicense( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const { orgId } = req.params; + + if (!orgId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Organization ID is required" + ) + ); + } + + logger.debug(`Generating new license for orgId: ${orgId}`); + + const licenseData = req.body; + const apiResponse = await createNewLicense(orgId, licenseData); + + return sendResponse(res, { + data: apiResponse.data, + success: apiResponse.success, + error: apiResponse.error, + message: apiResponse.message, + status: apiResponse.status + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred while generating new license" + ) + ); + } +} diff --git a/server/private/routers/generatedLicense/index.ts b/server/private/routers/generatedLicense/index.ts new file mode 100644 index 00000000..fa07430f --- /dev/null +++ b/server/private/routers/generatedLicense/index.ts @@ -0,0 +1,2 @@ +export * from "./listGeneratedLicenses"; +export * from "./generateNewLicense"; diff --git a/server/private/routers/generatedLicense/listGeneratedLicenses.ts b/server/private/routers/generatedLicense/listGeneratedLicenses.ts new file mode 100644 index 00000000..ee5f96be --- /dev/null +++ b/server/private/routers/generatedLicense/listGeneratedLicenses.ts @@ -0,0 +1,83 @@ +import { Request, Response, NextFunction } from "express"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { response as sendResponse } from "@server/lib/response"; +import privateConfig from "@server/private/lib/config"; + +export type GeneratedLicenseKey = { + instanceName: string | null; + licenseKey: string; + expiresAt: string; + isValid: boolean; + createdAt: string; + tier: string; + type: string; +}; + +export type ListGeneratedLicenseKeysResponse = GeneratedLicenseKey[]; + +async function fetchLicenseKeys(orgId: string): Promise { + try { + const response = await fetch( + `https://api.fossorial.io/api/v1/license-internal/enterprise/${orgId}/list`, + { + method: "GET", + headers: { + "api-key": + privateConfig.getRawPrivateConfig().server + .fossorial_api_key!, + "Content-Type": "application/json" + } + } + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching license keys:", error); + throw error; + } +} + +export async function listSaasLicenseKeys( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const { orgId } = req.params; + + if (!orgId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Organization ID is required" + ) + ); + } + + const apiResponse = await fetchLicenseKeys(orgId); + const keys: GeneratedLicenseKey[] = apiResponse.data.licenseKeys || []; + + return sendResponse(res, { + data: keys, + success: true, + error: false, + message: "Successfully retrieved license keys", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred while fetching license keys" + ) + ); + } +} diff --git a/server/private/routers/internal.ts b/server/private/routers/internal.ts index ab3db1ce..444d416e 100644 --- a/server/private/routers/internal.ts +++ b/server/private/routers/internal.ts @@ -15,6 +15,7 @@ import * as loginPage from "#private/routers/loginPage"; import * as auth from "#private/routers/auth"; import * as orgIdp from "#private/routers/orgIdp"; import * as billing from "#private/routers/billing"; +import * as license from "#private/routers/license"; import { Router } from "express"; import { verifySessionUserMiddleware } from "@server/middlewares"; @@ -34,3 +35,5 @@ internalRouter.post( verifySessionUserMiddleware, auth.getSessionTransferToken ); + +internalRouter.get(`/license/status`, license.getLicenseStatus); diff --git a/server/routers/license/activateLicense.ts b/server/private/routers/license/activateLicense.ts similarity index 93% rename from server/routers/license/activateLicense.ts rename to server/private/routers/license/activateLicense.ts index 832bc19d..f6d73f6f 100644 --- a/server/routers/license/activateLicense.ts +++ b/server/private/routers/license/activateLicense.ts @@ -3,9 +3,10 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import license, { LicenseStatus } from "@server/license/license"; +import license from "#private/license/license"; import { z } from "zod"; import { fromError } from "zod-validation-error"; +import { LicenseStatus } from "@server/license/license"; const bodySchema = z .object({ diff --git a/server/routers/license/deleteLicenseKey.ts b/server/private/routers/license/deleteLicenseKey.ts similarity index 92% rename from server/routers/license/deleteLicenseKey.ts rename to server/private/routers/license/deleteLicenseKey.ts index 37b74fee..bcee5b6a 100644 --- a/server/routers/license/deleteLicenseKey.ts +++ b/server/private/routers/license/deleteLicenseKey.ts @@ -8,9 +8,8 @@ import { fromError } from "zod-validation-error"; import { db } from "@server/db"; import { eq } from "drizzle-orm"; import { licenseKey } from "@server/db"; -import license, { LicenseStatus } from "@server/license/license"; -import { encrypt } from "@server/lib/crypto"; -import config from "@server/lib/config"; +import license from "#private/license/license"; +import { LicenseStatus } from "@server/license/license"; const paramsSchema = z .object({ diff --git a/server/routers/license/getLicenseStatus.ts b/server/private/routers/license/getLicenseStatus.ts similarity index 89% rename from server/routers/license/getLicenseStatus.ts rename to server/private/routers/license/getLicenseStatus.ts index e4f28882..b36d8dca 100644 --- a/server/routers/license/getLicenseStatus.ts +++ b/server/private/routers/license/getLicenseStatus.ts @@ -3,7 +3,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import license, { LicenseStatus } from "@server/license/license"; +import license from "#private/license/license"; +import { LicenseStatus } from "@server/license/license"; export type GetLicenseStatusResponse = LicenseStatus; diff --git a/server/routers/license/index.ts b/server/private/routers/license/index.ts similarity index 100% rename from server/routers/license/index.ts rename to server/private/routers/license/index.ts diff --git a/server/routers/license/listLicenseKeys.ts b/server/private/routers/license/listLicenseKeys.ts similarity index 89% rename from server/routers/license/listLicenseKeys.ts rename to server/private/routers/license/listLicenseKeys.ts index d106abd7..338c92c4 100644 --- a/server/routers/license/listLicenseKeys.ts +++ b/server/private/routers/license/listLicenseKeys.ts @@ -3,7 +3,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import license, { LicenseKeyCache } from "@server/license/license"; +import license from "#private/license/license"; +import { LicenseKeyCache } from "@server/license/license"; export type ListLicenseKeysResponse = LicenseKeyCache[]; diff --git a/server/routers/license/recheckStatus.ts b/server/private/routers/license/recheckStatus.ts similarity index 91% rename from server/routers/license/recheckStatus.ts rename to server/private/routers/license/recheckStatus.ts index cd4bf779..73e630c8 100644 --- a/server/routers/license/recheckStatus.ts +++ b/server/private/routers/license/recheckStatus.ts @@ -3,7 +3,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import license, { LicenseStatus } from "@server/license/license"; +import license from "#private/license/license"; +import { LicenseStatus } from "@server/license/license"; export type RecheckStatusResponse = LicenseStatus; diff --git a/server/routers/external.ts b/server/routers/external.ts index d90b7478..8bd72f62 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -13,7 +13,6 @@ import * as siteResource from "./siteResource"; import * as supporterKey from "./supporterKey"; import * as accessToken from "./accessToken"; import * as idp from "./idp"; -import * as license from "./license"; import * as apiKeys from "./apiKeys"; import HttpCode from "@server/types/HttpCode"; import { @@ -710,30 +709,6 @@ authenticated.get( authenticated.get("/idp", idp.listIdps); // anyone can see this; it's just a list of idp names and ids authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); -authenticated.post( - "/license/activate", - verifyUserIsServerAdmin, - license.activateLicense -); - -authenticated.get( - "/license/keys", - verifyUserIsServerAdmin, - license.listLicenseKeys -); - -authenticated.delete( - "/license/:licenseKey", - verifyUserIsServerAdmin, - license.deleteLicenseKey -); - -authenticated.post( - "/license/recheck", - verifyUserIsServerAdmin, - license.recheckStatus -); - authenticated.get( `/api-key/:apiKeyId`, verifyUserIsServerAdmin, diff --git a/server/routers/idp/createOidcIdp.ts b/server/routers/idp/createOidcIdp.ts index 223a08b8..67357d76 100644 --- a/server/routers/idp/createOidcIdp.ts +++ b/server/routers/idp/createOidcIdp.ts @@ -11,7 +11,6 @@ import { idp, idpOidcConfig, idpOrg, orgs } from "@server/db"; import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; -import license from "@server/license/license"; const paramsSchema = z.object({}).strict(); diff --git a/server/routers/idp/updateOidcIdp.ts b/server/routers/idp/updateOidcIdp.ts index 904d0d9e..53ece68e 100644 --- a/server/routers/idp/updateOidcIdp.ts +++ b/server/routers/idp/updateOidcIdp.ts @@ -11,7 +11,6 @@ import { idp, idpOidcConfig } from "@server/db"; import { eq } from "drizzle-orm"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; -import license from "@server/license/license"; const paramsSchema = z .object({ diff --git a/server/routers/internal.ts b/server/routers/internal.ts index 10966bb5..561408aa 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -5,7 +5,6 @@ import * as resource from "./resource"; import * as badger from "./badger"; import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; -import * as license from "@server/routers/license"; import * as idp from "@server/routers/idp"; import { proxyToRemote } from "@server/lib/remoteProxy"; import config from "@server/lib/config"; @@ -41,8 +40,6 @@ internalRouter.get( supporterKey.isSupporterKeyVisible ); -internalRouter.get(`/license/status`, license.getLicenseStatus); - internalRouter.get("/idp", idp.listIdps); internalRouter.get("/idp/:idpId", idp.getIdp); @@ -96,4 +93,4 @@ if (config.isManagedMode()) { ); } else { badgerRouter.post("/exchange-session", badger.exchangeSession); -} \ No newline at end of file +} diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index 317f6461..da995447 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -3,12 +3,10 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { response as sendResponse } from "@server/lib/response"; -import privateConfig from "#private/lib/config"; import config from "@server/lib/config"; import { db } from "@server/db"; import { count } from "drizzle-orm"; import { users } from "@server/db"; -import license from "@server/license/license"; import { build } from "@server/build"; export type IsSupporterKeyVisibleResponse = { @@ -29,12 +27,6 @@ export async function isSupporterKeyVisible( let visible = !hidden && key?.valid !== true; - const licenseStatus = await license.check(); - - if (licenseStatus.isLicenseValid) { - visible = false; - } - if (key?.tier === "Limited Supporter") { const [numUsers] = await db.select({ count: count() }).from(users); @@ -46,7 +38,7 @@ export async function isSupporterKeyVisible( } } - if (build != "oss") { + if (build !== "oss") { visible = false; } diff --git a/src/app/[orgId]/layout.tsx b/src/app/[orgId]/layout.tsx index d8f28a59..5d9a724a 100644 --- a/src/app/[orgId]/layout.tsx +++ b/src/app/[orgId]/layout.tsx @@ -9,7 +9,7 @@ import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import { cache } from "react"; import SetLastOrgCookie from "@app/components/SetLastOrgCookie"; -import PrivateSubscriptionStatusProvider from "@app/providers/SubscriptionStatusProvider"; +import SubscriptionStatusProvider from "@app/providers/SubscriptionStatusProvider"; import { GetOrgSubscriptionResponse } from "#private/routers/billing/getOrgSubscription"; import { pullEnv } from "@app/lib/pullEnv"; import { build } from "@server/build"; @@ -56,7 +56,7 @@ export default async function OrgLayout(props: { } let subscriptionStatus = null; - if (build != "oss") { + if (build === "saas") { try { const getSubscription = cache(() => internal.get>( @@ -73,13 +73,13 @@ export default async function OrgLayout(props: { } return ( - {props.children} - + ); } diff --git a/src/app/[orgId]/settings/(private)/billing/layout.tsx b/src/app/[orgId]/settings/(private)/billing/layout.tsx index 3bb60caf..538c7fde 100644 --- a/src/app/[orgId]/settings/(private)/billing/layout.tsx +++ b/src/app/[orgId]/settings/(private)/billing/layout.tsx @@ -60,13 +60,6 @@ export default async function BillingSettingsPage({ const t = await getTranslations(); - const navItems = [ - { - title: t('billing'), - href: `/{orgId}/settings/billing`, - }, - ]; - return ( <> diff --git a/src/app/[orgId]/settings/(private)/idp/page.tsx b/src/app/[orgId]/settings/(private)/idp/page.tsx index 2d1882b9..551de34f 100644 --- a/src/app/[orgId]/settings/(private)/idp/page.tsx +++ b/src/app/[orgId]/settings/(private)/idp/page.tsx @@ -45,7 +45,10 @@ export default async function OrgIdpPage(props: OrgIdpPageProps) { const subRes = await getSubscription(); subscriptionStatus = subRes.data.data; } catch {} - const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + const subscribed = + build === "enterprise" + ? true + : subscriptionStatus?.tier === TierId.STANDARD; return ( <> diff --git a/src/app/[orgId]/settings/(private)/license/layout.tsx b/src/app/[orgId]/settings/(private)/license/layout.tsx new file mode 100644 index 00000000..9083bb81 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/license/layout.tsx @@ -0,0 +1,42 @@ +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { verifySession } from "@app/lib/auth/verifySession"; +import { redirect } from "next/navigation"; +import { cache } from "react"; +import { getTranslations } from "next-intl/server"; +import { build } from "@server/build"; + +type LicensesSettingsProps = { + children: React.ReactNode; + params: Promise<{ orgId: string }>; +}; + +export default async function LicensesSetingsLayoutProps({ + children, + params +}: LicensesSettingsProps) { + const { orgId } = await params; + + if (build !== "saas") { + redirect(`/${orgId}/settings`); + } + + const getUser = cache(verifySession); + const user = await getUser(); + + if (!user) { + redirect(`/`); + } + + const t = await getTranslations(); + + return ( + <> + + + {children} + + ); +} diff --git a/src/app/[orgId]/settings/(private)/license/page.tsx b/src/app/[orgId]/settings/(private)/license/page.tsx new file mode 100644 index 00000000..627618f4 --- /dev/null +++ b/src/app/[orgId]/settings/(private)/license/page.tsx @@ -0,0 +1,25 @@ +import GenerateLicenseKeysTable from "@app/components/GenerateLicenseKeysTable"; +import { internal } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { ListGeneratedLicenseKeysResponse } from "@server/private/routers/generatedLicense"; +import { AxiosResponse } from "axios"; + +type Props = { + params: Promise<{ orgId: string }>; +}; + +export const dynamic = "force-dynamic"; + +export default async function Page({ params }: Props) { + const { orgId } = await params; + + let licenseKeys: ListGeneratedLicenseKeysResponse = []; + try { + const data = await internal.get< + AxiosResponse + >(`/org/${orgId}/license`, await authCookieHeader()); + licenseKeys = data.data.data; + } catch {} + + return ; +} diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index 2b6fddad..d789b2e2 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -77,9 +77,10 @@ export default function Page() { const t = useTranslations(); const subscription = useSubscriptionStatusContext(); - const subscribed = subscription?.getTier() === TierId.STANDARD; - const [selectedOption, setSelectedOption] = useState("internal"); + const [selectedOption, setSelectedOption] = useState( + "internal" + ); const [inviteLink, setInviteLink] = useState(null); const [loading, setLoading] = useState(false); const [expiresInDays, setExpiresInDays] = useState(1); @@ -204,7 +205,13 @@ export default function Page() { googleAzureForm.reset(); genericOidcForm.reset(); } - }, [selectedOption, env.email.emailEnabled, internalForm, googleAzureForm, genericOidcForm]); + }, [ + selectedOption, + env.email.emailEnabled, + internalForm, + googleAzureForm, + genericOidcForm + ]); useEffect(() => { if (!selectedOption) { @@ -232,7 +239,7 @@ export default function Page() { } async function fetchIdps() { - if (build === "saas" && !subscribed) { + if (build === "saas" && !subscription?.subscribed) { return; } @@ -345,7 +352,9 @@ export default function Page() { async function onSubmitGoogleAzure( values: z.infer ) { - const selectedUserOption = userOptions.find(opt => opt.id === selectedOption); + const selectedUserOption = userOptions.find( + (opt) => opt.id === selectedOption + ); if (!selectedUserOption?.idpId) return; setLoading(true); @@ -385,7 +394,9 @@ export default function Page() { async function onSubmitGenericOidc( values: z.infer ) { - const selectedUserOption = userOptions.find(opt => opt.id === selectedOption); + const selectedUserOption = userOptions.find( + (opt) => opt.id === selectedOption + ); if (!selectedUserOption?.idpId) return; setLoading(true); @@ -675,214 +686,284 @@ export default function Page() { )} - {selectedOption && selectedOption !== "internal" && dataLoaded && ( - - - - {t("userSettings")} - - - {t("userSettingsDescription")} - - - - - {/* Google/Azure Form */} - {(() => { - const selectedUserOption = userOptions.find(opt => opt.id === selectedOption); - return selectedUserOption?.variant === "google" || selectedUserOption?.variant === "azure"; - })() && ( -
- + + + {t("userSettings")} + + + {t("userSettingsDescription")} + + + + + {/* Google/Azure Form */} + {(() => { + const selectedUserOption = + userOptions.find( + (opt) => + opt.id === + selectedOption + ); + return ( + selectedUserOption?.variant === + "google" || + selectedUserOption?.variant === + "azure" + ); + })() && ( + + + ( + + + {t("email")} + + + + + + )} - className="space-y-4" - id="create-user-form" - > - ( - - - {t("email")} - - - - - - - )} - /> + /> - ( - - - {t("nameOptional")} - - - - - - - )} - /> + ( + + + {t( + "nameOptional" + )} + + + + + + + )} + /> - ( - - - {t("role")} - - + + + + + + + {roles.map( + ( + role + ) => ( - {role.name} + { + role.name + } - ))} - - - - - )} - /> - - - )} - - {/* Generic OIDC Form */} - {(() => { - const selectedUserOption = userOptions.find(opt => opt.id === selectedOption); - return selectedUserOption?.variant !== "google" && selectedUserOption?.variant !== "azure"; - })() && ( -
- + + + )} - className="space-y-4" - id="create-user-form" - > - ( - - - {t("username")} - - - - -

- {t("usernameUniq")} -

- -
- )} - /> + /> + + + )} - ( - - - {t("emailOptional")} - - - - - - - )} - /> + {/* Generic OIDC Form */} + {(() => { + const selectedUserOption = + userOptions.find( + (opt) => + opt.id === + selectedOption + ); + return ( + selectedUserOption?.variant !== + "google" && + selectedUserOption?.variant !== + "azure" + ); + })() && ( +
+ + ( + + + {t( + "username" + )} + + + + +

+ {t( + "usernameUniq" + )} +

+ +
+ )} + /> - ( - - - {t("nameOptional")} - - - - - - - )} - /> + ( + + + {t( + "emailOptional" + )} + + + + + + + )} + /> - ( - - - {t("role")} - - + + + + )} + /> + + ( + + + {t("role")} + + - - - )} - /> - - - )} -
-
-
- )} + ) + )} + + + + + )} + /> + + + )} + + + + )}
diff --git a/src/app/[orgId]/settings/clients/page.tsx b/src/app/[orgId]/settings/clients/page.tsx index 994b1d56..0813ad3c 100644 --- a/src/app/[orgId]/settings/clients/page.tsx +++ b/src/app/[orgId]/settings/clients/page.tsx @@ -5,6 +5,7 @@ import { ClientRow } from "../../../../components/ClientsTable"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { ListClientsResponse } from "@server/routers/client"; import ClientsTable from "../../../../components/ClientsTable"; +import { getTranslations } from "next-intl/server"; type ClientsPageProps = { params: Promise<{ orgId: string }>; @@ -13,6 +14,8 @@ type ClientsPageProps = { export const dynamic = "force-dynamic"; export default async function ClientsPage(props: ClientsPageProps) { + const t = await getTranslations(); + const params = await props.params; let clients: ListClientsResponse["clients"] = []; try { @@ -48,8 +51,8 @@ export default async function ClientsPage(props: ClientsPageProps) { return ( <> diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx index a7948536..1801bcf2 100644 --- a/src/app/[orgId]/settings/general/page.tsx +++ b/src/app/[orgId]/settings/general/page.tsx @@ -1,6 +1,8 @@ "use client"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; -import AuthPageSettings, { AuthPageSettingsRef } from "@app/components/private/AuthPageSettings"; +import AuthPageSettings, { + AuthPageSettingsRef +} from "@app/components/private/AuthPageSettings"; import { Button } from "@app/components/ui/button"; import { useOrgContext } from "@app/hooks/useOrgContext"; @@ -134,7 +136,10 @@ export default function GeneralPage() { }); // Also save auth page settings if they have unsaved changes - if (build === "saas" && authPageSettingsRef.current?.hasUnsavedChanges()) { + if ( + build === "saas" && + authPageSettingsRef.current?.hasUnsavedChanges() + ) { await authPageSettingsRef.current.saveAuthSettings(); } @@ -239,7 +244,9 @@ export default function GeneralPage() { - {build === "saas" && } + {(build === "saas") && ( + + )} {/* Save Button */}
@@ -276,7 +283,6 @@ export default function GeneralPage() { )} - ); } diff --git a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx index 6a088196..fb79c59a 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/authentication/page.tsx @@ -99,7 +99,6 @@ export default function ResourceAuthenticationPage() { const t = useTranslations(); const subscription = useSubscriptionStatusContext(); - const subscribed = subscription?.getTier() === TierId.STANDARD; const [pageLoading, setPageLoading] = useState(true); @@ -141,8 +140,10 @@ export default function ResourceAuthenticationPage() { useState(false); const [loadingRemoveResourcePincode, setLoadingRemoveResourcePincode] = useState(false); - const [loadingRemoveResourceHeaderAuth, setLoadingRemoveResourceHeaderAuth] = - useState(false); + const [ + loadingRemoveResourceHeaderAuth, + setLoadingRemoveResourceHeaderAuth + ] = useState(false); const [isSetPasswordOpen, setIsSetPasswordOpen] = useState(false); const [isSetPincodeOpen, setIsSetPincodeOpen] = useState(false); @@ -234,7 +235,7 @@ export default function ResourceAuthenticationPage() { ); if (build === "saas") { - if (subscribed) { + if (subscription?.subscribed) { setAllIdps( idpsResponse.data.data.idps.map((idp) => ({ id: idp.idpId, diff --git a/src/app/admin/license/layout.tsx b/src/app/admin/license/layout.tsx new file mode 100644 index 00000000..6c6e8baf --- /dev/null +++ b/src/app/admin/license/layout.tsx @@ -0,0 +1,17 @@ +import { build } from "@server/build"; +import { redirect } from "next/navigation"; + +export const dynamic = "force-dynamic"; + +interface LayoutProps { + children: React.ReactNode; +} + +export default async function AdminLicenseLayout(props: LayoutProps) { + if (build !== "enterprise") { + redirect(`/admin`); + } + + return props.children; +} + diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index a871b8e0..665212fc 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -31,7 +31,6 @@ import { CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; -import { useRouter } from "next/navigation"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { SettingsContainer, @@ -43,14 +42,10 @@ import { SettingsSectionFooter } from "@app/components/Settings"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { Badge } from "@app/components/ui/badge"; -import { Check, Heart, InfoIcon, ShieldCheck, ShieldOff } from "lucide-react"; +import { Check, Heart, InfoIcon } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; -import { Progress } from "@app/components/ui/progress"; -import { MinusCircle, PlusCircle } from "lucide-react"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { SitePriceCalculator } from "../../../components/SitePriceCalculator"; -import Link from "next/link"; import { Checkbox } from "@app/components/ui/checkbox"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; @@ -70,13 +65,11 @@ export default function LicensePage() { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selectedLicenseKey, setSelectedLicenseKey] = useState(null); - const router = useRouter(); + const { licenseStatus, updateLicenseStatus } = useLicenseStatusContext(); const [hostLicense, setHostLicense] = useState(null); const [isPurchaseModalOpen, setIsPurchaseModalOpen] = useState(false); - const [purchaseMode, setPurchaseMode] = useState< - "license" | "additional-sites" - >("license"); + const [purchaseMode, setPurchaseMode] = useState<"license">("license"); // Separate loading states for different actions const [isInitialLoading, setIsInitialLoading] = useState(true); @@ -90,10 +83,10 @@ export default function LicensePage() { const formSchema = z.object({ licenseKey: z .string() - .nonempty({ message: t('licenseKeyRequired') }) + .nonempty({ message: t("licenseKeyRequired") }) .max(255), agreeToTerms: z.boolean().refine((val) => val === true, { - message: t('licenseTermsAgree') + message: t("licenseTermsAgree") }) }); @@ -122,7 +115,7 @@ export default function LicensePage() { ); const keys = response.data.data; setRows(keys); - const hostKey = keys.find((key) => key.type === "HOST"); + const hostKey = keys.find((key) => key.type === "host"); if (hostKey) { setHostLicense(hostKey.licenseKey); } else { @@ -130,10 +123,10 @@ export default function LicensePage() { } } catch (e) { toast({ - title: t('licenseErrorKeyLoad'), + title: t("licenseErrorKeyLoad"), description: formatAxiosError( e, - t('licenseErrorKeyLoadDescription') + t("licenseErrorKeyLoadDescription") ) }); } @@ -149,16 +142,16 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: t('licenseKeyDeleted'), - description: t('licenseKeyDeletedDescription') + title: t("licenseKeyDeleted"), + description: t("licenseKeyDeletedDescription") }); setIsDeleteModalOpen(false); } catch (e) { toast({ - title: t('licenseErrorKeyDelete'), + title: t("licenseErrorKeyDelete"), description: formatAxiosError( e, - t('licenseErrorKeyDeleteDescription') + t("licenseErrorKeyDeleteDescription") ) }); } finally { @@ -175,15 +168,15 @@ export default function LicensePage() { } await loadLicenseKeys(); toast({ - title: t('licenseErrorKeyRechecked'), - description: t('licenseErrorKeyRecheckedDescription') + title: t("licenseErrorKeyRechecked"), + description: t("licenseErrorKeyRecheckedDescription") }); } catch (e) { toast({ - title: t('licenseErrorKeyRecheck'), + title: t("licenseErrorKeyRecheck"), description: formatAxiosError( e, - t('licenseErrorKeyRecheckDescription') + t("licenseErrorKeyRecheckDescription") ) }); } finally { @@ -202,8 +195,8 @@ export default function LicensePage() { } toast({ - title: t('licenseKeyActivated'), - description: t('licenseKeyActivatedDescription') + title: t("licenseKeyActivated"), + description: t("licenseKeyActivatedDescription") }); setIsCreateModalOpen(false); @@ -212,10 +205,10 @@ export default function LicensePage() { } catch (e) { toast({ variant: "destructive", - title: t('licenseErrorKeyActivate'), + title: t("licenseErrorKeyActivate"), description: formatAxiosError( e, - t('licenseErrorKeyActivateDescription') + t("licenseErrorKeyActivateDescription") ) }); } finally { @@ -246,9 +239,9 @@ export default function LicensePage() { > - {t('licenseActivateKey')} + {t("licenseActivateKey")} - {t('licenseActivateKeyDescription')} + {t("licenseActivateKeyDescription")} @@ -263,7 +256,9 @@ export default function LicensePage() { name="licenseKey" render={({ field }) => ( - {t('licenseKey')} + + {t("licenseKey")} + @@ -286,16 +281,7 @@ export default function LicensePage() {
- {t('licenseAgreement')} - {/*
*/} - {/* */} - {/* {t('fossorialLicense')} */} - {/* */} + {t("licenseAgreement")}
@@ -307,7 +293,7 @@ export default function LicensePage() {
- +
@@ -331,187 +317,98 @@ export default function LicensePage() { dialog={

- {t('licenseQuestionRemove', {selectedKey: obfuscateLicenseKey(selectedLicenseKey.licenseKey)})} + {t("licenseQuestionRemove", { + selectedKey: obfuscateLicenseKey( + selectedLicenseKey.licenseKey + ) + })}

- - {t('licenseMessageRemove')} - -

-

- {t('licenseMessageConfirm')} + {t("licenseMessageRemove")}

+

{t("licenseMessageConfirm")}

} - buttonText={t('licenseKeyDeleteConfirm')} + buttonText={t("licenseKeyDeleteConfirm")} onConfirm={async () => deleteLicenseKey(selectedLicenseKey.licenseKeyEncrypted) } string={selectedLicenseKey.licenseKey} - title={t('licenseKeyDelete')} + title={t("licenseKeyDelete")} /> )} - - - - {t('licenseAbout')} - - - {t('licenseAboutDescription')} - - + {/* */} + {/* */} + {/* */} + {/* {t("licenseAbout")} */} + {/* */} + {/* */} + {/* {t("licenseAboutDescription")} */} + {/* */} + {/* */} - - - - {t('licenseHost')} - - {t('licenseHostDescription')} - - -
-
- {licenseStatus?.isLicenseValid ? ( -
-
- - {licenseStatus?.tier === - "PROFESSIONAL" - ? t('licenseTierCommercial') - : licenseStatus?.tier === - "ENTERPRISE" - ? t('licenseTierCommercial') - : t('licensed')} -
+ + + {t("licenseHost")} + + {t("licenseHostDescription")} + + +
+
+ {licenseStatus?.isLicenseValid ? ( +
+
+ + {t("licensed")}
- ) : ( -
- {supporterStatus?.visible ? ( -
- {t('communityEdition')} -
- ) : ( -
- - {t('communityEdition')} -
- )} -
- )} -
- {licenseStatus?.hostId && ( -
-
- {t('hostId')} -
-
- )} - {hostLicense && ( -
-
- {t('licenseKey')} -
- -
- )} -
- - - - - - - {t('licenseSiteUsage')} - - {t('licenseSiteUsageDecsription')} - - -
-
+ ) : (
- {t('licenseSitesUsed', {count: licenseStatus?.usedSites || 0})} -
-
- {!licenseStatus?.isHostLicensed && ( -

- {t('licenseNoSiteLimit')} -

- )} - {licenseStatus?.maxSites && ( -
-
- - {t('licenseSitesUsedMax', {usedSites: licenseStatus.usedSites || 0, maxSites: licenseStatus.maxSites})} - - - {Math.round( - ((licenseStatus.usedSites || - 0) / - licenseStatus.maxSites) * - 100 - )} - % - -
- + {t("unlicensed")}
)}
- {/* */} - {/* {!licenseStatus?.isHostLicensed ? ( */} - {/* <> */} - {/* */} - {/* */} - {/* ) : ( */} - {/* <> */} - {/* */} - {/* */} - {/* )} */} - {/* */} -
- + {licenseStatus?.hostId && ( +
+
+ {t("hostId")} +
+ +
+ )} + {hostLicense && ( +
+
+ {t("licenseKey")} +
+ +
+ )} +
+ + + +
{ diff --git a/src/app/admin/managed/page.tsx b/src/app/admin/managed/page.tsx deleted file mode 100644 index f7a8f70b..00000000 --- a/src/app/admin/managed/page.tsx +++ /dev/null @@ -1,180 +0,0 @@ -"use client"; - -import { - SettingsContainer, - SettingsSection, - SettingsSectionTitle as SectionTitle, - SettingsSectionBody, - SettingsSectionFooter -} from "@app/components/Settings"; -import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { Alert } from "@app/components/ui/alert"; -import { Button } from "@app/components/ui/button"; -import { - Shield, - Zap, - RefreshCw, - Activity, - Wrench, - CheckCircle, - ExternalLink -} from "lucide-react"; -import Link from "next/link"; -import { useTranslations } from "next-intl"; - -export default function ManagedPage() { - const t = useTranslations(); - - return ( - <> - - - - - -

- {t("managedSelfHosted.introTitle")}{" "} - {t("managedSelfHosted.introDescription")} -

-

- {t("managedSelfHosted.introDetail")} -

- -
-
-
- -
-

- {t( - "managedSelfHosted.benefitSimplerOperations.title" - )} -

-

- {t( - "managedSelfHosted.benefitSimplerOperations.description" - )} -

-
-
- -
- -
-

- {t( - "managedSelfHosted.benefitAutomaticUpdates.title" - )} -

-

- {t( - "managedSelfHosted.benefitAutomaticUpdates.description" - )} -

-
-
- -
- -
-

- {t( - "managedSelfHosted.benefitLessMaintenance.title" - )} -

-

- {t( - "managedSelfHosted.benefitLessMaintenance.description" - )} -

-
-
-
- -
-
- -
-

- {t( - "managedSelfHosted.benefitCloudFailover.title" - )} -

-

- {t( - "managedSelfHosted.benefitCloudFailover.description" - )} -

-
-
-
- -
-

- {t( - "managedSelfHosted.benefitHighAvailability.title" - )} -

-

- {t( - "managedSelfHosted.benefitHighAvailability.description" - )} -

-
-
- -
- -
-

- {t( - "managedSelfHosted.benefitFutureEnhancements.title" - )} -

-

- {t( - "managedSelfHosted.benefitFutureEnhancements.description" - )} -

-
-
-
-
- - - {t("managedSelfHosted.docsAlert.text")}{" "} - - {t("managedSelfHosted.docsAlert.documentation")} - - - . - -
- - - - - -
-
- - ); -} diff --git a/src/app/auth/(private)/org/page.tsx b/src/app/auth/(private)/org/page.tsx index c438ba66..491aeb67 100644 --- a/src/app/auth/(private)/org/page.tsx +++ b/src/app/auth/(private)/org/page.tsx @@ -74,16 +74,21 @@ export default async function OrgAuthPage(props: { } let subscriptionStatus: GetOrgTierResponse | null = null; - try { - const getSubscription = cache(() => - priv.get>( - `/org/${loginPage!.orgId}/billing/tier` - ) - ); - const subRes = await getSubscription(); - subscriptionStatus = subRes.data.data; - } catch {} - const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + if (build === "saas") { + try { + const getSubscription = cache(() => + priv.get>( + `/org/${loginPage!.orgId}/billing/tier` + ) + ); + const subRes = await getSubscription(); + subscriptionStatus = subRes.data.data; + } catch {} + } + const subscribed = + build === "enterprise" + ? true + : subscriptionStatus?.tier === TierId.STANDARD; if (build === "saas" && !subscribed) { redirect(env.app.dashboardUrl); diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx index 0902baaa..88b0f07d 100644 --- a/src/app/auth/layout.tsx +++ b/src/app/auth/layout.tsx @@ -1,15 +1,5 @@ -import ProfileIcon from "@app/components/ProfileIcon"; import ThemeSwitcher from "@app/components/ThemeSwitcher"; -import { Separator } from "@app/components/ui/separator"; -import { priv } from "@app/lib/api"; -import { verifySession } from "@app/lib/auth/verifySession"; -import UserProvider from "@app/providers/UserProvider"; -import { GetLicenseStatusResponse } from "@server/routers/license"; -import { AxiosResponse } from "axios"; -import { ExternalLink } from "lucide-react"; import { Metadata } from "next"; -import { cache } from "react"; -import { getTranslations } from "next-intl/server"; export const metadata: Metadata = { title: `Auth - ${process.env.BRANDING_APP_NAME || "Pangolin"}`, @@ -21,19 +11,6 @@ type AuthLayoutProps = { }; export default async function AuthLayout({ children }: AuthLayoutProps) { - const getUser = cache(verifySession); - const user = await getUser(); - const t = await getTranslations(); - const hideFooter = true; - - const licenseStatusRes = await cache( - async () => - await priv.get>( - "/license/status" - ) - )(); - const licenseStatus = licenseStatusRes.data.data; - return (
); } diff --git a/src/app/auth/resource/[resourceGuid]/page.tsx b/src/app/auth/resource/[resourceGuid]/page.tsx index d37bc8ca..8789cd38 100644 --- a/src/app/auth/resource/[resourceGuid]/page.tsx +++ b/src/app/auth/resource/[resourceGuid]/page.tsx @@ -73,7 +73,10 @@ export default async function ResourceAuthPage(props: { subscriptionStatus = subRes.data.data; } catch {} } - const subscribed = subscriptionStatus?.tier === TierId.STANDARD; + const subscribed = + build === "enterprise" + ? true + : subscriptionStatus?.tier === TierId.STANDARD; const allHeaders = await headers(); const host = allHeaders.get("host"); @@ -207,7 +210,12 @@ export default async function ResourceAuthPage(props: { })) as LoginFormIDP[]; } - if (!userIsUnauthorized && isSSOOnly && authInfo.skipToIdpId && authInfo.skipToIdpId !== null) { + if ( + !userIsUnauthorized && + isSSOOnly && + authInfo.skipToIdpId && + authInfo.skipToIdpId !== null + ) { const idp = loginIdps.find((idp) => idp.idpId === authInfo.skipToIdpId); if (idp) { return ( diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 29d49300..2d6eb965 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,12 +11,13 @@ import { priv } from "@app/lib/api"; import { AxiosResponse } from "axios"; import { IsSupporterKeyVisibleResponse } from "@server/routers/supporterKey"; import LicenseStatusProvider from "@app/providers/LicenseStatusProvider"; -import { GetLicenseStatusResponse } from "@server/routers/license"; +import { GetLicenseStatusResponse } from "#private/routers/license"; import LicenseViolation from "@app/components/LicenseViolation"; import { cache } from "react"; import { NextIntlClientProvider } from "next-intl"; import { getLocale } from "next-intl/server"; import { Toaster } from "@app/components/ui/toaster"; +import { build } from "@server/build"; export const metadata: Metadata = { title: `Dashboard - ${process.env.BRANDING_APP_NAME || "Pangolin"}`, @@ -57,13 +58,22 @@ export default async function RootLayout({ supporterData.visible = res.data.data.visible; supporterData.tier = res.data.data.tier; - const licenseStatusRes = await cache( - async () => + let licenseStatus: GetLicenseStatusResponse; + if (build === "enterprise") { + const licenseStatusRes = await cache( + async () => await priv.get>( "/license/status" ) - )(); - const licenseStatus = licenseStatusRes.data.data; + )(); + licenseStatus = licenseStatusRes.data.data; + } else { + licenseStatus = { + isHostLicensed: false, + isLicenseValid: false, + hostId: "" + }; + } return ( diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 369de1d4..a3311b68 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -54,7 +54,8 @@ export const orgNavSections = ( { title: "sidebarClients", href: "/{orgId}/settings/clients", - icon: + icon: , + isBeta: true } ] : []), @@ -63,7 +64,8 @@ export const orgNavSections = ( { title: "sidebarRemoteExitNodes", href: "/{orgId}/settings/remote-exit-nodes", - icon: + icon: , + showEE: true } ] : []), @@ -97,7 +99,8 @@ export const orgNavSections = ( { title: "sidebarIdentityProviders", href: "/{orgId}/settings/idp", - icon: + icon: , + showEE: true } ] : []), @@ -116,15 +119,6 @@ export const orgNavSections = ( href: "/{orgId}/settings/api-keys", icon: }, - ...(build == "saas" - ? [ - { - title: "sidebarBilling", - href: "/{orgId}/settings/billing", - icon: - } - ] - : []), { title: "sidebarSettings", href: "/{orgId}/settings/general", @@ -138,15 +132,6 @@ export const adminNavSections: SidebarNavSection[] = [ { heading: "Admin", items: [ - ...(build == "oss" - ? [ - { - title: "managedSelfhosted", - href: "/admin/managed", - icon: - } - ] - : []), { title: "sidebarAllUsers", href: "/admin/users", diff --git a/src/components/GenerateLicenseKeyForm.tsx b/src/components/GenerateLicenseKeyForm.tsx new file mode 100644 index 00000000..dfb45a45 --- /dev/null +++ b/src/components/GenerateLicenseKeyForm.tsx @@ -0,0 +1,1384 @@ +"use client"; + +import { Button } from "@app/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import { Input } from "@app/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@app/components/ui/select"; +import { Checkbox } from "@app/components/ui/checkbox"; +import { toast } from "@app/hooks/useToast"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { AxiosResponse } from "axios"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import CopyTextBox from "@app/components/CopyTextBox"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import { formatAxiosError } from "@app/lib/api"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { GenerateNewLicenseResponse } from "@server/private/routers/generatedLicense/generateNewLicense"; +import { useTranslations } from "next-intl"; +import React from "react"; +import { StrategySelect, StrategyOption } from "./StrategySelect"; +import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; +import { InfoIcon, Check } from "lucide-react"; +import { useUserContext } from "@app/hooks/useUserContext"; + +type FormProps = { + open: boolean; + setOpen: (open: boolean) => void; + orgId: string; + onGenerated?: () => void; +}; + +export default function GenerateLicenseKeyForm({ + open, + setOpen, + orgId, + onGenerated +}: FormProps) { + const t = useTranslations(); + const { env } = useEnvContext(); + const api = createApiClient({ env }); + + const { user } = useUserContext(); + + const [currentStep, setCurrentStep] = useState(1); + const [loading, setLoading] = useState(false); + const [generatedKey, setGeneratedKey] = useState(null); + const [formKey, setFormKey] = useState(0); + + // Step 1: Email & License Type + const step1Schema = z.object({ + email: z + .string() + .email(t("generateLicenseKeyForm.validation.emailRequired")), + useCaseType: z.enum(["personal", "business"], { + required_error: t( + "generateLicenseKeyForm.validation.useCaseTypeRequired" + ) + }) + }); + + // Step 2: Personal Information + const createStep2Schema = (useCaseType: string | undefined) => + z + .object({ + firstName: z + .string() + .min( + 1, + t("generateLicenseKeyForm.validation.firstNameRequired") + ), + lastName: z + .string() + .min( + 1, + t("generateLicenseKeyForm.validation.lastNameRequired") + ), + jobTitle: z.string().optional(), + primaryUse: z + .string() + .min( + 1, + t( + "generateLicenseKeyForm.validation.primaryUseRequired" + ) + ), + industry: z.string().optional(), + prospectiveUsers: z.coerce.number().optional(), + prospectiveSites: z.coerce.number().optional() + }) + .refine( + (data) => { + // If business use case, job title is required + if (useCaseType === "business") { + return data.jobTitle; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.jobTitleRequiredBusiness" + ), + path: ["jobTitle"] + } + ) + .refine( + (data) => { + // If business use case, industry is required + if (useCaseType === "business") { + return data.industry; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.industryRequiredBusiness" + ), + path: ["industry"] + } + ); + + // Step 3: Contact Information + const createStep3Schema = (useCaseType: string | undefined) => + z + .object({ + stateProvinceRegion: z + .string() + .min( + 1, + t( + "generateLicenseKeyForm.validation.stateProvinceRegionRequired" + ) + ), + postalZipCode: z + .string() + .min( + 1, + t( + "generateLicenseKeyForm.validation.postalZipCodeRequired" + ) + ), + country: z.string().optional(), + phoneNumber: z.string().optional(), + companyName: z.string().optional(), + countryOfResidence: z.string().optional(), + companyWebsite: z.string().optional(), + companyPhoneNumber: z.string().optional() + }) + .refine( + (data) => { + // If business use case, company name is required + if (useCaseType === "business") { + return data.companyName; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.companyNameRequiredBusiness" + ), + path: ["companyName"] + } + ) + .refine( + (data) => { + // If business use case, country of residence is required + if (useCaseType === "business") { + return data.countryOfResidence; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.countryOfResidenceRequiredBusiness" + ), + path: ["countryOfResidence"] + } + ) + .refine( + (data) => { + // If personal use case, country is required + if (useCaseType === "personal" && !data.country) { + return false; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.countryRequiredPersonal" + ), + path: ["country"] + } + ); + + // Step 4: Terms & Generate + const step4Schema = z.object({ + agreedToTerms: z + .boolean() + .refine( + (val) => val === true, + t("generateLicenseKeyForm.validation.agreeToTermsRequired") + ), + complianceConfirmed: z + .boolean() + .refine( + (val) => val === true, + t("generateLicenseKeyForm.validation.complianceConfirmationRequired") + ) + }); + + // Complete form schema for final submission with conditional validation + const createFormSchema = (useCaseType: string | undefined) => + z + .object({ + email: z.string().email("Please enter a valid email address"), + useCaseType: z.enum(["personal", "business"]), + firstName: z.string().min(1, "First name is required"), + lastName: z.string().min(1, "Last name is required"), + jobTitle: z.string().optional(), + primaryUse: z + .string() + .min(1, "Please describe your primary use"), + industry: z.string().optional(), + prospectiveUsers: z.coerce.number().optional(), + prospectiveSites: z.coerce.number().optional(), + stateProvinceRegion: z + .string() + .min( + 1, + t( + "generateLicenseKeyForm.validation.stateProvinceRegionRequired" + ) + ), + postalZipCode: z + .string() + .min( + 1, + t( + "generateLicenseKeyForm.validation.postalZipCodeRequired" + ) + ), + country: z.string().optional(), + phoneNumber: z.string().optional(), + companyName: z.string().optional(), + countryOfResidence: z.string().optional(), + companyWebsite: z.string().optional(), + companyPhoneNumber: z.string().optional(), + agreedToTerms: z + .boolean() + .refine( + (val) => val === true, + t( + "generateLicenseKeyForm.validation.agreeToTermsRequired" + ) + ), + complianceConfirmed: z + .boolean() + .refine( + (val) => val === true, + t("generateLicenseKeyForm.validation.complianceConfirmationRequired") + ) + }) + .refine( + (data) => { + // If business use case, job title is required + if (useCaseType === "business") { + return data.jobTitle; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.jobTitleRequiredBusiness" + ), + path: ["jobTitle"] + } + ) + .refine( + (data) => { + // If business use case, industry is required + if (useCaseType === "business") { + return data.industry; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.industryRequiredBusiness" + ), + path: ["industry"] + } + ) + .refine( + (data) => { + // If business use case, company name is required + if (useCaseType === "business") { + return data.companyName; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.companyNameRequiredBusiness" + ), + path: ["companyName"] + } + ) + .refine( + (data) => { + // If business use case, country of residence is required + if (useCaseType === "business") { + return data.countryOfResidence; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.countryOfResidenceRequiredBusiness" + ), + path: ["countryOfResidence"] + } + ) + .refine( + (data) => { + // If personal use case, country is required + if (useCaseType === "personal") { + return data.country; + } + return true; + }, + { + message: t( + "generateLicenseKeyForm.validation.countryRequiredPersonal" + ), + path: ["country"] + } + ); + + type FormData = z.infer>; + + // Base schema for form initialization (without conditional validation) + const baseFormSchema = z.object({ + email: z.string().email("Please enter a valid email address"), + useCaseType: z.enum(["personal", "business"]), + firstName: z.string().min(1, "First name is required"), + lastName: z.string().min(1, "Last name is required"), + jobTitle: z.string().optional(), + primaryUse: z.string().min(1, "Please describe your primary use"), + industry: z.string().optional(), + prospectiveUsers: z.coerce.number().optional(), + prospectiveSites: z.coerce.number().optional(), + stateProvinceRegion: z + .string() + .min(1, "State/Province/Region is required"), + postalZipCode: z.string().min(1, "Postal/ZIP Code is required"), + country: z.string().optional(), + phoneNumber: z.string().optional(), + companyName: z.string().optional(), + countryOfResidence: z.string().optional(), + companyWebsite: z.string().optional(), + companyPhoneNumber: z.string().optional(), + agreedToTerms: z + .boolean() + .refine( + (val) => val === true, + t("generateLicenseKeyForm.validation.agreeToTermsRequired") + ), + complianceConfirmed: z + .boolean() + .refine( + (val) => val === true, + t("generateLicenseKeyForm.validation.complianceConfirmationRequired") + ) + }); + + const form = useForm({ + resolver: zodResolver(baseFormSchema), + defaultValues: { + email: user?.email || "", + useCaseType: undefined, + firstName: "", + lastName: "", + jobTitle: "", + primaryUse: "", + industry: "", + prospectiveUsers: undefined, + prospectiveSites: undefined, + stateProvinceRegion: "", + postalZipCode: "", + country: "", + phoneNumber: "", + companyName: "", + countryOfResidence: "", + companyWebsite: "", + companyPhoneNumber: "", + agreedToTerms: false, + complianceConfirmed: false + } + }); + + const useCaseType = form.watch("useCaseType"); + const [previousUseCaseType, setPreviousUseCaseType] = useState< + string | undefined + >(undefined); + + // Reset form when use case type changes + React.useEffect(() => { + if ( + useCaseType !== previousUseCaseType && + useCaseType && + previousUseCaseType + ) { + // Reset fields that are specific to use case type + form.setValue("jobTitle", ""); + form.setValue("prospectiveUsers", undefined); + form.setValue("prospectiveSites", undefined); + form.setValue("companyName", ""); + form.setValue("countryOfResidence", ""); + form.setValue("companyWebsite", ""); + form.setValue("companyPhoneNumber", ""); + form.setValue("phoneNumber", ""); + form.setValue("country", ""); + + setPreviousUseCaseType(useCaseType); + } + }, [useCaseType, previousUseCaseType, form]); + + // Reset form when dialog opens + React.useEffect(() => { + if (open) { + form.reset({ + email: user?.email || "", + useCaseType: undefined, + firstName: "", + lastName: "", + jobTitle: "", + primaryUse: "", + industry: "", + prospectiveUsers: undefined, + prospectiveSites: undefined, + stateProvinceRegion: "", + postalZipCode: "", + country: "", + phoneNumber: "", + companyName: "", + countryOfResidence: "", + companyWebsite: "", + companyPhoneNumber: "", + agreedToTerms: false, + complianceConfirmed: false + }); + setCurrentStep(1); + setGeneratedKey(null); + setPreviousUseCaseType(undefined); + } + }, [open, form, user?.email]); + + const useCaseOptions: StrategyOption<"personal" | "business">[] = [ + { + id: "personal", + title: t("generateLicenseKeyForm.useCaseOptions.personal.title"), + description: ( +
+

+ {t( + "generateLicenseKeyForm.useCaseOptions.personal.description" + )} +

+
    +
  • + + + Home-lab enthusiasts and self-hosting hobbyists + +
  • +
  • + + + Personal projects, learning, and experimentation + +
  • +
  • + + + Individual developers and tech enthusiasts + +
  • +
+
+ ) + }, + { + id: "business", + title: t("generateLicenseKeyForm.useCaseOptions.business.title"), + description: ( +
+

+ {t( + "generateLicenseKeyForm.useCaseOptions.business.description" + )} +

+
    +
  • + + + Companies, startups, and organizations + +
  • +
  • + + + Professional services and client work + +
  • +
  • + + + Revenue-generating or commercial use cases + +
  • +
+
+ ) + } + ]; + + const steps = [ + { + title: t("generateLicenseKeyForm.steps.emailLicenseType.title"), + description: t( + "generateLicenseKeyForm.steps.emailLicenseType.description" + ) + }, + { + title: t("generateLicenseKeyForm.steps.personalInformation.title"), + description: t( + "generateLicenseKeyForm.steps.personalInformation.description" + ) + }, + { + title: t("generateLicenseKeyForm.steps.contactInformation.title"), + description: t( + "generateLicenseKeyForm.steps.contactInformation.description" + ) + }, + { + title: t("generateLicenseKeyForm.steps.termsGenerate.title"), + description: t( + "generateLicenseKeyForm.steps.termsGenerate.description" + ) + } + ]; + + const nextStep = async () => { + let isValid = false; + + try { + // Validate current step based on step number + switch (currentStep) { + case 1: + await step1Schema.parseAsync(form.getValues()); + isValid = true; + break; + case 2: + await createStep2Schema( + form.getValues("useCaseType") + ).parseAsync(form.getValues()); + isValid = true; + break; + case 3: + await createStep3Schema( + form.getValues("useCaseType") + ).parseAsync(form.getValues()); + isValid = true; + break; + case 4: + await step4Schema.parseAsync(form.getValues()); + isValid = true; + break; + default: + isValid = false; + } + } catch (error) { + if (error instanceof z.ZodError) { + // Set form errors for the current step fields + error.errors.forEach((err) => { + const fieldName = err.path[0] as keyof FormData; + form.setError(fieldName, { + type: "manual", + message: err.message + }); + }); + } + return; + } + + if (isValid && currentStep < steps.length) { + setCurrentStep(currentStep + 1); + } + }; + + const prevStep = () => { + if (currentStep > 1) { + setCurrentStep(currentStep - 1); + } + }; + + const onSubmit = async (values: FormData) => { + // Validate with the dynamic schema before submission + try { + await createFormSchema(values.useCaseType).parseAsync(values); + } catch (error) { + if (error instanceof z.ZodError) { + // Set form errors for any validation failures + error.errors.forEach((err) => { + const fieldName = err.path[0] as keyof FormData; + form.setError(fieldName, { + type: "manual", + message: err.message + }); + }); + return; + } + } + + setLoading(true); + try { + const payload = { + email: values.email, + useCaseType: values.useCaseType, + personal: + values.useCaseType === "personal" + ? { + firstName: values.firstName, + lastName: values.lastName, + aboutYou: { + primaryUse: values.primaryUse + }, + personalInfo: { + stateProvinceRegion: + values.stateProvinceRegion, + postalZipCode: values.postalZipCode, + country: values.country, + phoneNumber: values.phoneNumber || "" + } + } + : undefined, + business: + values.useCaseType === "business" + ? { + firstName: values.firstName, + lastName: values.lastName, + jobTitle: values.jobTitle || "", + aboutYou: { + primaryUse: values.primaryUse, + industry: values.industry, + prospectiveUsers: + values.prospectiveUsers || undefined, + prospectiveSites: + values.prospectiveSites || undefined + }, + companyInfo: { + companyName: values.companyName || "", + countryOfResidence: + values.countryOfResidence || "", + stateProvinceRegion: + values.stateProvinceRegion, + postalZipCode: values.postalZipCode, + companyWebsite: values.companyWebsite || "", + companyPhoneNumber: + values.companyPhoneNumber || "" + } + } + : undefined, + consent: { + agreedToTerms: values.agreedToTerms, + acknowledgedPrivacyPolicy: values.agreedToTerms, + complianceConfirmed: values.complianceConfirmed + } + }; + + const response = await api.put< + AxiosResponse + >(`/org/${orgId}/license`, payload); + + if (response.data.data?.licenseKey?.licenseKey) { + setGeneratedKey(response.data.data.licenseKey.licenseKey); + onGenerated?.(); + toast({ + title: t("generateLicenseKeyForm.toasts.success.title"), + description: t( + "generateLicenseKeyForm.toasts.success.description" + ), + variant: "default" + }); + } + } catch (e) { + console.error(e); + toast({ + title: t("generateLicenseKeyForm.toasts.error.title"), + description: formatAxiosError( + e, + t("generateLicenseKeyForm.toasts.error.description") + ), + variant: "destructive" + }); + } + setLoading(false); + }; + + const handleClose = () => { + setOpen(false); + setCurrentStep(1); + setGeneratedKey(null); + setFormKey((prev) => prev + 1); // Force form reset by changing key + form.reset({ + email: user?.email || "", + useCaseType: undefined, + firstName: "", + lastName: "", + jobTitle: "", + primaryUse: "", + industry: "", + prospectiveUsers: undefined, + prospectiveSites: undefined, + stateProvinceRegion: "", + postalZipCode: "", + country: "", + phoneNumber: "", + companyName: "", + countryOfResidence: "", + companyWebsite: "", + companyPhoneNumber: "", + agreedToTerms: false, + complianceConfirmed: false + }); + }; + + const renderStepContent = () => { + switch (currentStep) { + case 1: + return ( +
+ + + + {t( + "generateLicenseKeyForm.alerts.commercialUseDisclosure.title" + )} + + + {t( + "generateLicenseKeyForm.alerts.commercialUseDisclosure.description" + )} + + + + ( + + + {t( + "generateLicenseKeyForm.form.useCaseQuestion" + )} + + { + field.onChange(value); + // Reset form when use case type changes + form.reset({ + email: user?.email || "", + useCaseType: value, + firstName: "", + lastName: "", + jobTitle: "", + primaryUse: "", + industry: "", + prospectiveUsers: undefined, + prospectiveSites: undefined, + stateProvinceRegion: "", + postalZipCode: "", + country: "", + phoneNumber: "", + companyName: "", + countryOfResidence: "", + companyWebsite: "", + companyPhoneNumber: "", + agreedToTerms: false, + complianceConfirmed: false + }); + }} + cols={2} + /> + + + )} + /> +
+ ); + + case 2: + return ( +
+
+ ( + + + {t( + "generateLicenseKeyForm.form.firstName" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.lastName" + )} + + + + + + + )} + /> +
+ + {useCaseType === "business" && ( + ( + + + {t( + "generateLicenseKeyForm.form.jobTitle" + )} + + + + + + + )} + /> + )} + +
+ ( + + + {t( + "generateLicenseKeyForm.form.primaryUseQuestion" + )} + + + + + + + )} + /> + + {useCaseType === "business" && ( + <> + ( + + + {t( + "generateLicenseKeyForm.form.industryQuestion" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.prospectiveUsersQuestion" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.prospectiveSitesQuestion" + )} + + + + + + + )} + /> + + )} +
+
+ ); + + case 3: + return ( +
+ {useCaseType === "business" && ( +
+ ( + + + {t( + "generateLicenseKeyForm.form.companyName" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.countryOfResidence" + )} + + + + + + + )} + /> + +
+ ( + + + {t( + "generateLicenseKeyForm.form.stateProvinceRegion" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.postalZipCode" + )} + + + + + + + )} + /> +
+ +
+ ( + + + {t( + "generateLicenseKeyForm.form.companyWebsite" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.companyPhoneNumber" + )} + + + + + + + )} + /> +
+
+ )} + + {useCaseType === "personal" && ( +
+
+ ( + + + {t( + "generateLicenseKeyForm.form.stateProvinceRegion" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.postalZipCode" + )} + + + + + + + )} + /> +
+ +
+ ( + + + {t( + "generateLicenseKeyForm.form.country" + )} + + + + + + + )} + /> + + ( + + + {t( + "generateLicenseKeyForm.form.phoneNumberOptional" + )} + + + + + + + )} + /> +
+
+ )} +
+ ); + + case 4: + return ( +
+ ( + + + + +
+ +
+ {t("signUpTerms.IAgreeToThe")}{" "} + + {t( + "signUpTerms.termsOfService" + )}{" "} + + {t("signUpTerms.and")}{" "} + + {t( + "signUpTerms.privacyPolicy" + )} + +
+
+ +
+
+ )} + /> + + ( + + + + +
+ +
+ I confirm that I am in compliance with the{" "} + + Fossorial Commercial License + {" "} + and that reporting inaccurate information or misidentifying use of the product is a violation of the license. +
+
+ +
+
+ )} + /> +
+ ); + + default: + return null; + } + }; + + return ( + + + + {t("generateLicenseKey")} + + {steps[currentStep - 1]?.description} + + + +
+ {/* Progress indicator */} +
+ {steps.map((step, index) => ( +
+
+ {index + 1} +
+ + {step.title} + +
+ ))} +
+ + {generatedKey ? ( +
+ {useCaseType === "business" && ( + + + {t( + "generateLicenseKeyForm.alerts.trialPeriodInformation.title" + )} + + + {t( + "generateLicenseKeyForm.alerts.trialPeriodInformation.description" + )} + + + )} + + +
+ ) : ( +
+ + {renderStepContent()} +
+ + )} +
+
+ + + + + + {!generatedKey && ( + <> + {currentStep > 1 && ( + + )} + + {currentStep < steps.length ? ( + + ) : ( + + )} + + )} + +
+
+ ); +} diff --git a/src/components/GenerateLicenseKeysTable.tsx b/src/components/GenerateLicenseKeysTable.tsx new file mode 100644 index 00000000..374edaa5 --- /dev/null +++ b/src/components/GenerateLicenseKeysTable.tsx @@ -0,0 +1,192 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { ColumnDef } from "@tanstack/react-table"; +import { Button } from "./ui/button"; +import { ArrowUpDown } from "lucide-react"; +import CopyToClipboard from "./CopyToClipboard"; +import { Badge } from "./ui/badge"; +import moment from "moment"; +import { DataTable } from "./ui/data-table"; +import { GeneratedLicenseKey } from "@server/private/routers/generatedLicense"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { toast } from "@app/hooks/useToast"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { AxiosResponse } from "axios"; +import { GenerateNewLicenseResponse } from "@server/private/routers/generatedLicense/generateNewLicense"; +import GenerateLicenseKeyForm from "./GenerateLicenseKeyForm"; + +type GnerateLicenseKeysTableProps = { + licenseKeys: GeneratedLicenseKey[]; + orgId: string; +}; + +function obfuscateLicenseKey(key: string): string { + if (key.length <= 8) return key; + const firstPart = key.substring(0, 4); + const lastPart = key.substring(key.length - 4); + return `${firstPart}••••••••••••••••••••${lastPart}`; +} + +export default function GenerateLicenseKeysTable({ + licenseKeys, + orgId +}: GnerateLicenseKeysTableProps) { + const t = useTranslations(); + const router = useRouter(); + + const { env } = useEnvContext(); + const api = createApiClient({ env }); + + const [isRefreshing, setIsRefreshing] = useState(false); + const [showGenerateForm, setShowGenerateForm] = useState(false); + + const handleLicenseGenerated = () => { + // Refresh the data after license is generated + refreshData(); + }; + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + + const columns: ColumnDef[] = [ + { + accessorKey: "licenseKey", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const licenseKey = row.original.licenseKey; + return ( + + ); + } + }, + { + accessorKey: "instanceName", + cell: ({ row }) => { + return row.original.instanceName || "-"; + } + }, + { + accessorKey: "valid", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + return row.original.isValid ? ( + {t("yes")} + ) : ( + {t("no")} + ); + } + }, + { + accessorKey: "type", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const tier = row.original.tier; + return tier === "enterprise" + ? t("licenseTierEnterprise") + : t("licenseTierPersonal"); + } + }, + { + accessorKey: "terminateAt", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const termianteAt = row.original.expiresAt; + return moment(termianteAt).format("lll"); + } + } + ]; + + return ( + <> + { + setShowGenerateForm(true); + }} + /> + + + + ); +} diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx index dafa31a9..04cee5c8 100644 --- a/src/components/LayoutSidebar.tsx +++ b/src/components/LayoutSidebar.tsx @@ -6,7 +6,15 @@ import { OrgSelector } from "@app/components/OrgSelector"; import { cn } from "@app/lib/cn"; import { ListUserOrgsResponse } from "@server/routers/org"; import SupporterStatus from "@app/components/SupporterStatus"; -import { ExternalLink, Server, BookOpenText, Zap } from "lucide-react"; +import { + ExternalLink, + Server, + BookOpenText, + Zap, + CreditCard, + FileText, + TicketCheck +} from "lucide-react"; import { FaDiscord, FaGithub } from "react-icons/fa"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -22,6 +30,7 @@ import { TooltipTrigger } from "@app/components/ui/tooltip"; import { build } from "@server/build"; +import SidebarLicenseButton from "./SidebarLicenseButton"; interface LayoutSidebarProps { orgId?: string; @@ -119,8 +128,78 @@ export function LayoutSidebar({ />
+
- + {build === "saas" && ( +
+
+ + + + + {!isSidebarCollapsed && ( + {t("sidebarBilling")} + )} + + + + + + {!isSidebarCollapsed && ( + {t("sidebarEnterpriseLicenses")} + )} + +
+
+ )} + {build === "enterprise" && ( +
+ +
+ )} + {build === "oss" && ( +
+ +
+ )} {!isSidebarCollapsed && (
{loadFooterLinks() ? ( @@ -159,9 +238,9 @@ export function LayoutSidebar({ rel="noopener noreferrer" className="flex items-center justify-center gap-1" > - {!isUnlocked() + {build === "oss" ? t("communityEdition") - : t("commercialEdition")} + : t("enterpriseEdition")}
diff --git a/src/components/LicenseKeysDataTable.tsx b/src/components/LicenseKeysDataTable.tsx index 1def304b..71b15681 100644 --- a/src/components/LicenseKeysDataTable.tsx +++ b/src/components/LicenseKeysDataTable.tsx @@ -6,9 +6,9 @@ import { Button } from "@app/components/ui/button"; import { Badge } from "@app/components/ui/badge"; import { LicenseKeyCache } from "@server/license/license"; import { ArrowUpDown } from "lucide-react"; -import moment from "moment"; import CopyToClipboard from "@app/components/CopyToClipboard"; import { useTranslations } from "next-intl"; +import moment from "moment"; type LicenseKeysDataTableProps = { licenseKeys: LicenseKeyCache[]; @@ -28,7 +28,6 @@ export function LicenseKeysDataTable({ onDelete, onCreate }: LicenseKeysDataTableProps) { - const t = useTranslations(); const columns: ColumnDef[] = [ @@ -42,7 +41,7 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - {t('licenseKey')} + {t("licenseKey")} ); @@ -67,13 +66,17 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - {t('valid')} + {t("valid")} ); }, cell: ({ row }) => { - return row.original.valid ? t('yes') : t('no'); + return row.original.valid ? ( + {t("yes")} + ) : ( + {t("no")} + ); } }, { @@ -86,23 +89,20 @@ export function LicenseKeysDataTable({ column.toggleSorting(column.getIsSorted() === "asc") } > - {t('type')} + {t("type")} ); }, cell: ({ row }) => { - const type = row.original.type; - const label = - type === "SITES" ? t('sitesAdditional') : t('licenseHost'); - const variant = type === "SITES" ? "secondary" : "default"; - return row.original.valid ? ( - {label} - ) : null; + const tier = row.original.tier; + tier === "enterprise" + ? t("licenseTierEnterprise") + : t("licenseTierPersonal"); } }, { - accessorKey: "numSites", + accessorKey: "terminateAt", header: ({ column }) => { return ( ); + }, + cell: ({ row }) => { + const termianteAt = row.original.terminateAt; + return moment(termianteAt).format("lll"); } }, { @@ -125,7 +129,7 @@ export function LicenseKeysDataTable({ variant="secondary" onClick={() => onDelete(row.original)} > - {t('delete')} + {t("delete")}
) @@ -137,11 +141,11 @@ export function LicenseKeysDataTable({ columns={columns} data={licenseKeys} persistPageSize="licenseKeys-table" - title={t('licenseKeys')} - searchPlaceholder={t('licenseKeySearch')} + title={t("licenseKeys")} + searchPlaceholder={t("licenseKeySearch")} searchColumn="licenseKey" onAdd={onCreate} - addButtonText={t('licenseKeyAdd')} + addButtonText={t("licenseKeyAdd")} /> ); } diff --git a/src/components/LicenseViolation.tsx b/src/components/LicenseViolation.tsx index ea025e4c..c5f7504d 100644 --- a/src/components/LicenseViolation.tsx +++ b/src/components/LicenseViolation.tsx @@ -32,29 +32,5 @@ export default function LicenseViolation() { ); } - // Show usage violation banner - if ( - licenseStatus.maxSites && - licenseStatus.usedSites && - licenseStatus.usedSites > licenseStatus.maxSites - ) { - return ( -
-
-

- {t('componentsLicenseViolation', {usedSites: licenseStatus.usedSites, maxSites: licenseStatus.maxSites})} -

- -
-
- ); - } - return null; } diff --git a/src/components/SidebarLicenseButton.tsx b/src/components/SidebarLicenseButton.tsx new file mode 100644 index 00000000..597b761a --- /dev/null +++ b/src/components/SidebarLicenseButton.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@app/components/ui/tooltip"; +import { Button } from "./ui/button"; +import { TicketCheck } from "lucide-react"; +import { useTranslations } from "next-intl"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; +import Link from "next/link"; + +interface SidebarLicenseButtonProps { + isCollapsed?: boolean; +} + +export default function SidebarLicenseButton({ + isCollapsed = false +}: SidebarLicenseButtonProps) { + const { licenseStatus, updateLicenseStatus } = useLicenseStatusContext(); + + const t = useTranslations(); + + return ( + <> + {!licenseStatus?.isHostLicensed ? ( + isCollapsed ? ( + + + + + + + + + Enable Enterprise License + + + + ) : ( + + + + ) + ) : null} + + ); +} diff --git a/src/components/SidebarNav.tsx b/src/components/SidebarNav.tsx index 7e8ad336..7aaebfff 100644 --- a/src/components/SidebarNav.tsx +++ b/src/components/SidebarNav.tsx @@ -14,12 +14,14 @@ import { TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; +import { build } from "@server/build"; export type SidebarNavItem = { href: string; title: string; icon?: React.ReactNode; - showProfessional?: boolean; + showEE?: boolean; + isBeta?: boolean; }; export type SidebarNavSection = { @@ -71,7 +73,7 @@ export function SidebarNav({ isDisabled: boolean ) => { const tooltipText = - item.showProfessional && !isUnlocked() + item.showEE && !isUnlocked() ? `${t(item.title)} (${t("licenseBadge")})` : t(item.title); @@ -106,11 +108,24 @@ export function SidebarNav({ {!isCollapsed && ( <> {t(item.title)} - {item.showProfessional && !isUnlocked() && ( - - {t("licenseBadge")} + {item.isBeta && ( + + {t("beta")} )} + {build === "enterprise" && + item.showEE && + !isUnlocked() && ( + + {t("licenseBadge")} + + )} )} @@ -154,9 +169,11 @@ export function SidebarNav({ {section.items.map((item) => { const hydratedHref = hydrateHref(item.href); const isActive = pathname.startsWith(hydratedHref); - const isProfessional = - item.showProfessional && !isUnlocked(); - const isDisabled = disabled || isProfessional; + const isEE = + build === "enterprise" && + item.showEE && + !isUnlocked(); + const isDisabled = disabled || isEE; return renderNavItem( item, hydratedHref, diff --git a/src/components/SitesTable.tsx b/src/components/SitesTable.tsx index 0c7c2b48..476fd336 100644 --- a/src/components/SitesTable.tsx +++ b/src/components/SitesTable.tsx @@ -303,7 +303,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { return (
{originalRow.exitNodeName} - {build == "saas" && originalRow.exitNodeName && + {build == "saas" && originalRow.exitNodeName && ['mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'].includes(originalRow.exitNodeName.toLowerCase()) && ( Cloud )} diff --git a/src/components/StrategySelect.tsx b/src/components/StrategySelect.tsx index 63f1ce53..1b922cd1 100644 --- a/src/components/StrategySelect.tsx +++ b/src/components/StrategySelect.tsx @@ -7,7 +7,7 @@ import { useState, ReactNode } from "react"; export interface StrategyOption { id: TValue; title: string; - description: string; + description: string | ReactNode; disabled?: boolean; icon?: ReactNode; } @@ -68,7 +68,7 @@ export function StrategySelect({
{option.title}
- {option.description} + {typeof option.description === 'string' ? option.description : option.description}
diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 46c5ede0..61e0d018 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -72,454 +72,475 @@ export interface AuthPageSettingsRef { hasUnsavedChanges: () => boolean; } -const AuthPageSettings = forwardRef(({ - onSaveSuccess, - onSaveError -}, ref) => { - const { org } = useOrgContext(); - const api = createApiClient(useEnvContext()); - const router = useRouter(); - const t = useTranslations(); +const AuthPageSettings = forwardRef( + ({ onSaveSuccess, onSaveError }, ref) => { + const { org } = useOrgContext(); + const api = createApiClient(useEnvContext()); + const router = useRouter(); + const t = useTranslations(); - const subscription = useSubscriptionStatusContext(); - const subscribed = subscription?.getTier() === TierId.STANDARD; + const subscription = useSubscriptionStatusContext(); - // Auth page domain state - const [loginPage, setLoginPage] = useState( - null - ); - const [loginPageExists, setLoginPageExists] = useState(false); - const [editDomainOpen, setEditDomainOpen] = useState(false); - const [baseDomains, setBaseDomains] = useState([]); - const [selectedDomain, setSelectedDomain] = useState<{ - domainId: string; - subdomain?: string; - fullDomain: string; - baseDomain: string; - } | null>(null); - const [loadingLoginPage, setLoadingLoginPage] = useState(true); - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [loadingSave, setLoadingSave] = useState(false); + // Auth page domain state + const [loginPage, setLoginPage] = useState( + null + ); + const [loginPageExists, setLoginPageExists] = useState(false); + const [editDomainOpen, setEditDomainOpen] = useState(false); + const [baseDomains, setBaseDomains] = useState([]); + const [selectedDomain, setSelectedDomain] = useState<{ + domainId: string; + subdomain?: string; + fullDomain: string; + baseDomain: string; + } | null>(null); + const [loadingLoginPage, setLoadingLoginPage] = useState(true); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [loadingSave, setLoadingSave] = useState(false); - const form = useForm({ - resolver: zodResolver(AuthPageFormSchema), - defaultValues: { - authPageDomainId: loginPage?.domainId || "", - authPageSubdomain: loginPage?.subdomain || "" - }, - mode: "onChange" - }); + const form = useForm({ + resolver: zodResolver(AuthPageFormSchema), + defaultValues: { + authPageDomainId: loginPage?.domainId || "", + authPageSubdomain: loginPage?.subdomain || "" + }, + mode: "onChange" + }); - // Expose save function to parent component - useImperativeHandle(ref, () => ({ - saveAuthSettings: async () => { - await form.handleSubmit(onSubmit)(); - }, - hasUnsavedChanges: () => hasUnsavedChanges - }), [form, hasUnsavedChanges]); + // Expose save function to parent component + useImperativeHandle( + ref, + () => ({ + saveAuthSettings: async () => { + await form.handleSubmit(onSubmit)(); + }, + hasUnsavedChanges: () => hasUnsavedChanges + }), + [form, hasUnsavedChanges] + ); - // Fetch login page and domains data - useEffect(() => { - if (build !== "saas") { - return; - } - - const fetchLoginPage = async () => { - try { - const res = await api.get>( - `/org/${org?.org.orgId}/login-page` - ); - if (res.status === 200) { - setLoginPage(res.data.data); - setLoginPageExists(true); - // Update form with login page data - form.setValue( - "authPageDomainId", - res.data.data.domainId || "" - ); - form.setValue( - "authPageSubdomain", - res.data.data.subdomain || "" - ); - } - } catch (err) { - // Login page doesn't exist yet, that's okay - setLoginPage(null); - setLoginPageExists(false); - } finally { - setLoadingLoginPage(false); - } - }; - - const fetchDomains = async () => { - try { - const res = await api.get>( - `/org/${org?.org.orgId}/domains/` - ); - if (res.status === 200) { - const rawDomains = res.data.data.domains as DomainRow[]; - const domains = rawDomains.map((domain) => ({ - ...domain, - baseDomain: toUnicode(domain.baseDomain) - })); - setBaseDomains(domains); - } - } catch (err) { - console.error("Failed to fetch domains:", err); - } - }; - - if (org?.org.orgId) { - fetchLoginPage(); - fetchDomains(); - } - }, []); - - // Handle domain selection from modal - function handleDomainSelection(domain: { - domainId: string; - subdomain?: string; - fullDomain: string; - baseDomain: string; - }) { - form.setValue("authPageDomainId", domain.domainId); - form.setValue("authPageSubdomain", domain.subdomain || ""); - setEditDomainOpen(false); - - // Update loginPage state to show the selected domain immediately - const sanitizedSubdomain = domain.subdomain - ? finalizeSubdomainSanitize(domain.subdomain) - : ""; - - const sanitizedFullDomain = sanitizedSubdomain - ? `${sanitizedSubdomain}.${domain.baseDomain}` - : domain.baseDomain; - - // Only update loginPage state if a login page already exists - if (loginPageExists && loginPage) { - setLoginPage({ - ...loginPage, - domainId: domain.domainId, - subdomain: sanitizedSubdomain, - fullDomain: sanitizedFullDomain - }); - } - - setHasUnsavedChanges(true); - } - - // Clear auth page domain - function clearAuthPageDomain() { - form.setValue("authPageDomainId", ""); - form.setValue("authPageSubdomain", ""); - setLoginPage(null); - setHasUnsavedChanges(true); - } - - async function onSubmit(data: AuthPageFormValues) { - setLoadingSave(true); - - try { - // Handle auth page domain - if (data.authPageDomainId) { - if (build !== "saas" || (build === "saas" && subscribed)) { - const sanitizedSubdomain = data.authPageSubdomain - ? finalizeSubdomainSanitize(data.authPageSubdomain) - : ""; - - if (loginPageExists) { - // Login page exists on server - need to update it - // First, we need to get the loginPageId from the server since loginPage might be null locally - let loginPageId: number; - - if (loginPage) { - // We have the loginPage data locally - loginPageId = loginPage.loginPageId; - } else { - // User cleared selection locally, but login page still exists on server - // We need to fetch it to get the loginPageId - const fetchRes = await api.get< - AxiosResponse - >(`/org/${org?.org.orgId}/login-page`); - loginPageId = fetchRes.data.data.loginPageId; - } - - // Update existing auth page domain - const updateRes = await api.post( - `/org/${org?.org.orgId}/login-page/${loginPageId}`, - { - domainId: data.authPageDomainId, - subdomain: sanitizedSubdomain || null - } - ); - - if (updateRes.status === 201) { - setLoginPage(updateRes.data.data); - setLoginPageExists(true); - } - } else { - // No login page exists on server - create new one - const createRes = await api.put( - `/org/${org?.org.orgId}/login-page`, - { - domainId: data.authPageDomainId, - subdomain: sanitizedSubdomain || null - } - ); - - if (createRes.status === 201) { - setLoginPage(createRes.data.data); - setLoginPageExists(true); - } - } - } - } else if (loginPageExists) { - // Delete existing auth page domain if no domain selected - let loginPageId: number; - - if (loginPage) { - // We have the loginPage data locally - loginPageId = loginPage.loginPageId; - } else { - // User cleared selection locally, but login page still exists on server - // We need to fetch it to get the loginPageId - const fetchRes = await api.get< + // Fetch login page and domains data + useEffect(() => { + const fetchLoginPage = async () => { + try { + const res = await api.get< AxiosResponse >(`/org/${org?.org.orgId}/login-page`); - loginPageId = fetchRes.data.data.loginPageId; + if (res.status === 200) { + setLoginPage(res.data.data); + setLoginPageExists(true); + // Update form with login page data + form.setValue( + "authPageDomainId", + res.data.data.domainId || "" + ); + form.setValue( + "authPageSubdomain", + res.data.data.subdomain || "" + ); + } + } catch (err) { + // Login page doesn't exist yet, that's okay + setLoginPage(null); + setLoginPageExists(false); + } finally { + setLoadingLoginPage(false); } + }; - await api.delete( - `/org/${org?.org.orgId}/login-page/${loginPageId}` - ); - setLoginPage(null); - setLoginPageExists(false); + const fetchDomains = async () => { + try { + const res = await api.get< + AxiosResponse + >(`/org/${org?.org.orgId}/domains/`); + if (res.status === 200) { + const rawDomains = res.data.data.domains as DomainRow[]; + const domains = rawDomains.map((domain) => ({ + ...domain, + baseDomain: toUnicode(domain.baseDomain) + })); + setBaseDomains(domains); + } + } catch (err) { + console.error("Failed to fetch domains:", err); + } + }; + + if (org?.org.orgId) { + fetchLoginPage(); + fetchDomains(); + } + }, []); + + // Handle domain selection from modal + function handleDomainSelection(domain: { + domainId: string; + subdomain?: string; + fullDomain: string; + baseDomain: string; + }) { + form.setValue("authPageDomainId", domain.domainId); + form.setValue("authPageSubdomain", domain.subdomain || ""); + setEditDomainOpen(false); + + // Update loginPage state to show the selected domain immediately + const sanitizedSubdomain = domain.subdomain + ? finalizeSubdomainSanitize(domain.subdomain) + : ""; + + const sanitizedFullDomain = sanitizedSubdomain + ? `${sanitizedSubdomain}.${domain.baseDomain}` + : domain.baseDomain; + + // Only update loginPage state if a login page already exists + if (loginPageExists && loginPage) { + setLoginPage({ + ...loginPage, + domainId: domain.domainId, + subdomain: sanitizedSubdomain, + fullDomain: sanitizedFullDomain + }); } - setHasUnsavedChanges(false); - router.refresh(); - onSaveSuccess?.(); - } catch (e) { - toast({ - variant: "destructive", - title: t("authPageErrorUpdate"), - description: formatAxiosError(e, t("authPageErrorUpdateMessage")) - }); - onSaveError?.(e); - } finally { - setLoadingSave(false); + setHasUnsavedChanges(true); } - } - return ( - <> - - - - {t("authPage")} - - - {t("authPageDescription")} - - - - {build === "saas" && !subscribed ? ( - - - {t("orgAuthPageDisabled")}{" "} - {t("subscriptionRequiredToUse")} - - - ) : null} + // Clear auth page domain + function clearAuthPageDomain() { + form.setValue("authPageDomainId", ""); + form.setValue("authPageSubdomain", ""); + setLoginPage(null); + setHasUnsavedChanges(true); + } - - {loadingLoginPage ? ( -
-
- {t("loading")} + async function onSubmit(data: AuthPageFormValues) { + setLoadingSave(true); + + try { + // Handle auth page domain + if (data.authPageDomainId) { + if ( + build === "enterprise" || + (build === "saas" && subscription?.subscribed) + ) { + const sanitizedSubdomain = data.authPageSubdomain + ? finalizeSubdomainSanitize(data.authPageSubdomain) + : ""; + + if (loginPageExists) { + // Login page exists on server - need to update it + // First, we need to get the loginPageId from the server since loginPage might be null locally + let loginPageId: number; + + if (loginPage) { + // We have the loginPage data locally + loginPageId = loginPage.loginPageId; + } else { + // User cleared selection locally, but login page still exists on server + // We need to fetch it to get the loginPageId + const fetchRes = await api.get< + AxiosResponse + >(`/org/${org?.org.orgId}/login-page`); + loginPageId = fetchRes.data.data.loginPageId; + } + + // Update existing auth page domain + const updateRes = await api.post( + `/org/${org?.org.orgId}/login-page/${loginPageId}`, + { + domainId: data.authPageDomainId, + subdomain: sanitizedSubdomain || null + } + ); + + if (updateRes.status === 201) { + setLoginPage(updateRes.data.data); + setLoginPageExists(true); + } + } else { + // No login page exists on server - create new one + const createRes = await api.put( + `/org/${org?.org.orgId}/login-page`, + { + domainId: data.authPageDomainId, + subdomain: sanitizedSubdomain || null + } + ); + + if (createRes.status === 201) { + setLoginPage(createRes.data.data); + setLoginPageExists(true); + } + } + } + } else if (loginPageExists) { + // Delete existing auth page domain if no domain selected + let loginPageId: number; + + if (loginPage) { + // We have the loginPage data locally + loginPageId = loginPage.loginPageId; + } else { + // User cleared selection locally, but login page still exists on server + // We need to fetch it to get the loginPageId + const fetchRes = await api.get< + AxiosResponse + >(`/org/${org?.org.orgId}/login-page`); + loginPageId = fetchRes.data.data.loginPageId; + } + + await api.delete( + `/org/${org?.org.orgId}/login-page/${loginPageId}` + ); + setLoginPage(null); + setLoginPageExists(false); + } + + setHasUnsavedChanges(false); + router.refresh(); + onSaveSuccess?.(); + } catch (e) { + toast({ + variant: "destructive", + title: t("authPageErrorUpdate"), + description: formatAxiosError( + e, + t("authPageErrorUpdateMessage") + ) + }); + onSaveError?.(e); + } finally { + setLoadingSave(false); + } + } + + return ( + <> + + + + {t("authPage")} + + + {t("authPageDescription")} + + + + {build === "saas" && !subscription?.subscribed ? ( + + + {t("orgAuthPageDisabled")}{" "} + {t("subscriptionRequiredToUse")} + + + ) : null} + + + {loadingLoginPage ? ( +
+
+ {t("loading")} +
-
- ) : ( -
- -
- -
- - - {loginPage && - !loginPage.domainId ? ( - - ) : loginPage?.fullDomain ? ( - - {`${window.location.protocol}//${loginPage.fullDomain}`} - - ) : form.watch( - "authPageDomainId" - ) ? ( - // Show selected domain from form state when no loginPage exists yet - (() => { - const selectedDomainId = - form.watch( - "authPageDomainId" + ) : ( + + +
+ +
+ + + {loginPage && + !loginPage.domainId ? ( + + ) : loginPage?.fullDomain ? ( + + {`${window.location.protocol}//${loginPage.fullDomain}`} + + ) : form.watch( + "authPageDomainId" + ) ? ( + // Show selected domain from form state when no loginPage exists yet + (() => { + const selectedDomainId = + form.watch( + "authPageDomainId" + ); + const selectedSubdomain = + form.watch( + "authPageSubdomain" + ); + const domain = + baseDomains.find( + (d) => + d.domainId === + selectedDomainId + ); + if (domain) { + const sanitizedSubdomain = + selectedSubdomain + ? finalizeSubdomainSanitize( + selectedSubdomain + ) + : ""; + const fullDomain = + sanitizedSubdomain + ? `${sanitizedSubdomain}.${domain.baseDomain}` + : domain.baseDomain; + return fullDomain; + } + return t( + "noDomainSet" ); - const selectedSubdomain = - form.watch( - "authPageSubdomain" - ); - const domain = - baseDomains.find( - (d) => - d.domainId === - selectedDomainId - ); - if (domain) { - const sanitizedSubdomain = - selectedSubdomain - ? finalizeSubdomainSanitize( - selectedSubdomain - ) - : ""; - const fullDomain = - sanitizedSubdomain - ? `${sanitizedSubdomain}.${domain.baseDomain}` - : domain.baseDomain; - return fullDomain; - } - return t("noDomainSet"); - })() - ) : ( - t("noDomainSet") - )} - -
- - {form.watch("authPageDomainId") && ( + })() + ) : ( + t("noDomainSet") + )} + +
- )} + {form.watch( + "authPageDomainId" + ) && ( + + )} +
-
- {/* Certificate Status */} - {(build !== "saas" || - (build === "saas" && subscribed)) && - loginPage?.domainId && - loginPage?.fullDomain && - !hasUnsavedChanges && ( - + {/* Certificate Status */} + {(build === "enterprise" || + (build === "saas" && + subscription?.subscribed)) && + loginPage?.domainId && + loginPage?.fullDomain && + !hasUnsavedChanges && ( + + )} + + {!form.watch( + "authPageDomainId" + ) && ( +
+ {t( + "addDomainToEnableCustomAuthPages" + )} +
)} +
+ + + )} + + + - {!form.watch("authPageDomainId") && ( -
- {t( - "addDomainToEnableCustomAuthPages" - )} -
- )} -
- - - )} - - - + {/* Domain Picker Modal */} + setEditDomainOpen(setOpen)} + > + + + + {loginPage + ? t("editAuthPageDomain") + : t("setAuthPageDomain")} + + + {t("selectDomainForOrgAuthPage")} + + + + { + const selected = { + domainId: res.domainId, + subdomain: res.subdomain, + fullDomain: res.fullDomain, + baseDomain: res.baseDomain + }; + setSelectedDomain(selected); + }} + /> + + + + + + + + + + + ); + } +); - {/* Domain Picker Modal */} - setEditDomainOpen(setOpen)} - > - - - - {loginPage - ? t("editAuthPageDomain") - : t("setAuthPageDomain")} - - - {t("selectDomainForOrgAuthPage")} - - - - { - const selected = { - domainId: res.domainId, - subdomain: res.subdomain, - fullDomain: res.fullDomain, - baseDomain: res.baseDomain - }; - setSelectedDomain(selected); - }} - /> - - - - - - - - - - - ); -}); +AuthPageSettings.displayName = "AuthPageSettings"; -AuthPageSettings.displayName = 'AuthPageSettings'; - -export default AuthPageSettings; \ No newline at end of file +export default AuthPageSettings; diff --git a/src/contexts/subscriptionStatusContext.ts b/src/contexts/subscriptionStatusContext.ts index 267c58af..687a96bc 100644 --- a/src/contexts/subscriptionStatusContext.ts +++ b/src/contexts/subscriptionStatusContext.ts @@ -6,6 +6,8 @@ type SubscriptionStatusContextType = { updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void; isActive: () => boolean; getTier: () => string | null; + isSubscribed: () => boolean; + subscribed: boolean; }; const SubscriptionStatusContext = createContext< diff --git a/src/providers/LicenseStatusProvider.tsx b/src/providers/LicenseStatusProvider.tsx index 1f8d99c8..1196128d 100644 --- a/src/providers/LicenseStatusProvider.tsx +++ b/src/providers/LicenseStatusProvider.tsx @@ -40,13 +40,6 @@ export function LicenseStatusProvider({ ) { return true; } - if ( - licenseStatusState?.maxSites && - licenseStatusState?.usedSites && - licenseStatusState.usedSites > licenseStatusState.maxSites - ) { - return true; - } return false; }; diff --git a/src/providers/SubscriptionStatusProvider.tsx b/src/providers/SubscriptionStatusProvider.tsx index 71a9401c..d85193b2 100644 --- a/src/providers/SubscriptionStatusProvider.tsx +++ b/src/providers/SubscriptionStatusProvider.tsx @@ -1,9 +1,10 @@ "use client"; import SubscriptionStatusContext from "@app/contexts/subscriptionStatusContext"; -import { getTierPriceSet } from "@server/lib/billing/tiers"; +import { getTierPriceSet, TierId } from "@server/lib/billing/tiers"; import { GetOrgSubscriptionResponse } from "#private/routers/billing"; import { useState } from "react"; +import { build } from "@server/build"; interface ProviderProps { children: React.ReactNode; @@ -12,7 +13,7 @@ interface ProviderProps { sandbox_mode: boolean; } -export function PrivateSubscriptionStatusProvider({ +export function SubscriptionStatusProvider({ children, subscriptionStatus, env, @@ -21,7 +22,9 @@ export function PrivateSubscriptionStatusProvider({ const [subscriptionStatusState, setSubscriptionStatusState] = useState(subscriptionStatus); - const updateSubscriptionStatus = (updatedSubscriptionStatus: GetOrgSubscriptionResponse) => { + const updateSubscriptionStatus = ( + updatedSubscriptionStatus: GetOrgSubscriptionResponse + ) => { setSubscriptionStatusState((prev) => { return { ...updatedSubscriptionStatus @@ -43,7 +46,9 @@ export function PrivateSubscriptionStatusProvider({ // Iterate through tiers in order (earlier keys are higher tiers) for (const [tierId, priceId] of Object.entries(tierPriceSet)) { // Check if any subscription item matches this tier's price ID - const matchingItem = subscriptionStatus.items.find(item => item.priceId === priceId); + const matchingItem = subscriptionStatus.items.find( + (item) => item.priceId === priceId + ); if (matchingItem) { return tierId; } @@ -54,13 +59,24 @@ export function PrivateSubscriptionStatusProvider({ return null; }; + const isSubscribed = () => { + if (build === "enterprise") { + return true; + } + return getTier() === TierId.STANDARD; + }; + + const [subscribed, setSubscribed] = useState(isSubscribed()); + return ( {children} @@ -68,4 +84,4 @@ export function PrivateSubscriptionStatusProvider({ ); } -export default PrivateSubscriptionStatusProvider; +export default SubscriptionStatusProvider; diff --git a/tsconfig.json b/tsconfig.json index e32eabd3..0b856fe0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ "#private/*": ["../server/private/*"], "#open/*": ["../server/*"], "#closed/*": ["../server/private/*"], - "#dynamic/*": ["../server/*"] + "#dynamic/*": ["../server/private/*"] }, "plugins": [ { From 9c91a8db46851a5ea3df33747af572584b25ab99 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 13 Oct 2025 11:49:48 -0700 Subject: [PATCH 221/322] Update build process --- Dockerfile | 13 +++++++++++- package.json | 1 + server/db/pg/driver.ts | 12 +++++------ server/lib/readConfigFile.ts | 7 +------ server/private/lib/exitNodes/exitNodeComms.ts | 20 +++++++++---------- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index edee74eb..f704370c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,9 +15,20 @@ RUN echo "export * from \"./$DATABASE\";" > server/db/index.ts RUN echo "export const build = \"$BUILD\" as any;" > server/build.ts +# if the build is oss then remove the server/private directory +RUN if [ "$BUILD" = "oss" ]; then rm -rf server/private; fi + RUN if [ "$DATABASE" = "pg" ]; then npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init; else npx drizzle-kit generate --dialect $DATABASE --schema ./server/db/$DATABASE/schema.ts --out init; fi -RUN npm run build:$DATABASE +RUN mkdir -p dist +RUN npm run next:build +RUN node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD +RUN if [ "$DATABASE" = "pg" ]; then \ + node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs; \ + else \ + node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs; \ + fi + RUN npm run build:cli FROM node:22-alpine AS runner diff --git a/package.json b/package.json index 61126ec5..4484ee89 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "set:enterprise": "echo 'export const build = \"enterprise\" as any;' > server/build.ts", "set:sqlite": "echo 'export * from \"./sqlite\";' > server/db/index.ts", "set:pg": "echo 'export * from \"./pg\";' > server/db/index.ts", + "next:build": "next build", "build:sqlite": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs", "build:pg": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs", "start": "ENVIRONMENT=prod node dist/migrations.mjs && ENVIRONMENT=prod NODE_ENV=development node --enable-source-maps dist/server.mjs", diff --git a/server/db/pg/driver.ts b/server/db/pg/driver.ts index 23904c7e..6dbef7e8 100644 --- a/server/db/pg/driver.ts +++ b/server/db/pg/driver.ts @@ -38,9 +38,9 @@ function createDb() { const poolConfig = config.postgres.pool; const primaryPool = new Pool({ connectionString, - max: poolConfig.max_connections, - idleTimeoutMillis: poolConfig.idle_timeout_ms, - connectionTimeoutMillis: poolConfig.connection_timeout_ms, + max: poolConfig?.max_connections || 20, + idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, + connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000, }); const replicas = []; @@ -51,9 +51,9 @@ function createDb() { for (const conn of replicaConnections) { const replicaPool = new Pool({ connectionString: conn.connection_string, - max: poolConfig.max_replica_connections, - idleTimeoutMillis: poolConfig.idle_timeout_ms, - connectionTimeoutMillis: poolConfig.connection_timeout_ms, + max: poolConfig?.max_replica_connections || 20, + idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, + connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000, }); replicas.push(DrizzlePostgres(replicaPool)); } diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index baffaba1..d614103a 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -178,12 +178,7 @@ export const configSchema = z .default(5000) }) .optional() - .default({ - max_connections: 20, - max_replica_connections: 10, - idle_timeout_ms: 30000, - connection_timeout_ms: 5000 - }) + .default({}) }) .optional(), traefik: z diff --git a/server/private/lib/exitNodes/exitNodeComms.ts b/server/private/lib/exitNodes/exitNodeComms.ts index 4cf8fbe7..20c850a1 100644 --- a/server/private/lib/exitNodes/exitNodeComms.ts +++ b/server/private/lib/exitNodes/exitNodeComms.ts @@ -57,13 +57,13 @@ export async function sendToExitNode( } else { let hostname = exitNode.reachableAt; - logger.debug(`Exit node details:`, { - type: exitNode.type, - name: exitNode.name, - reachableAt: exitNode.reachableAt, - }); + // logger.debug(`Exit node details:`, { + // type: exitNode.type, + // name: exitNode.name, + // reachableAt: exitNode.reachableAt, + // }); - logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); + // logger.debug(`Configured local exit node name: ${config.getRawConfig().gerbil.exit_node_name}`); if (exitNode.name == config.getRawConfig().gerbil.exit_node_name) { hostname = privateConfig.getRawPrivateConfig().gerbil.local_exit_node_reachable_at; @@ -75,10 +75,10 @@ export async function sendToExitNode( ); } - logger.debug(`Sending request to exit node at ${hostname}`, { - type: request.remoteType, - data: request.data - }); + // logger.debug(`Sending request to exit node at ${hostname}`, { + // type: request.remoteType, + // data: request.data + // }); // Handle local exit node with HTTP API const method = request.method || "POST"; From cfa82b51fb1dcc628a70362914396ed45e16d7b4 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sat, 4 Oct 2025 12:20:46 +0530 Subject: [PATCH 222/322] refresh button in clients page --- src/components/ClientsDataTable.tsx | 8 +++- src/components/ClientsTable.tsx | 74 +++++++++++++++++++---------- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/components/ClientsDataTable.tsx b/src/components/ClientsDataTable.tsx index 6242ba05..619f1fad 100644 --- a/src/components/ClientsDataTable.tsx +++ b/src/components/ClientsDataTable.tsx @@ -8,13 +8,17 @@ import { DataTable } from "@app/components/ui/data-table"; interface DataTableProps { columns: ColumnDef[]; data: TData[]; + onRefresh?: () => void; + isRefreshing?: boolean; addClient?: () => void; } export function ClientsDataTable({ columns, data, - addClient + addClient, + onRefresh, + isRefreshing }: DataTableProps) { return ( ({ searchPlaceholder="Search clients..." searchColumn="name" onAdd={addClient} + onRefresh={onRefresh} + isRefreshing={isRefreshing} addButtonText="Add Client" /> ); diff --git a/src/components/ClientsTable.tsx b/src/components/ClientsTable.tsx index fc7c7c84..425b8395 100644 --- a/src/components/ClientsTable.tsx +++ b/src/components/ClientsTable.tsx @@ -25,6 +25,7 @@ import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; export type ClientRow = { id: number; @@ -53,6 +54,25 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) { const [rows, setRows] = useState(clients); const api = createApiClient(useEnvContext()); + const [isRefreshing, setIsRefreshing] = useState(false); + const t = useTranslations(); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; const deleteClient = (clientId: number) => { api.delete(`/client/${clientId}`) @@ -207,32 +227,32 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) { return (
- - - - - - {/* */} - {/* */} - {/* View settings */} - {/* */} - {/* */} - { - setSelectedClient(clientRow); - setIsDeleteModalOpen(true); - }} - > - Delete - - - + + + + + + {/* */} + {/* */} + {/* View settings */} + {/* */} + {/* */} + { + setSelectedClient(clientRow); + setIsDeleteModalOpen(true); + }} + > + Delete + + + @@ -292,6 +312,8 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) { addClient={() => { router.push(`/${orgId}/settings/clients/create`); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); From ccd27733311e84430e9a66e0b615cf0a81a765b0 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sat, 4 Oct 2025 12:58:30 +0530 Subject: [PATCH 223/322] refresh button on resources page --- src/components/ResourcesTable.tsx | 39 +++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/components/ResourcesTable.tsx b/src/components/ResourcesTable.tsx index 7a645bc7..ad60c685 100644 --- a/src/components/ResourcesTable.tsx +++ b/src/components/ResourcesTable.tsx @@ -24,7 +24,8 @@ import { MoreHorizontal, ArrowUpRight, ShieldOff, - ShieldCheck + ShieldCheck, + RefreshCw } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -179,9 +180,27 @@ export default function ResourcesTable({ const [internalColumnFilters, setInternalColumnFilters] = useState([]); const [internalGlobalFilter, setInternalGlobalFilter] = useState([]); + const [isRefreshing, setIsRefreshing] = useState(false); const currentView = searchParams.get("view") || defaultView; + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + useEffect(() => { const fetchSites = async () => { try { @@ -753,7 +772,23 @@ export default function ResourcesTable({ )}
- {getActionButton()} +
+ {refreshData && ( + + )} +
+
+ {getActionButton()} +
From b1e212721e2db320dbd6082a6f78f5eea2523e3a Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sat, 4 Oct 2025 13:31:48 +0530 Subject: [PATCH 224/322] refresh button for role, user, share-link, invitation table --- src/components/InvitationsDataTable.tsx | 8 +++++++- src/components/InvitationsTable.tsx | 27 ++++++++++++++++++++++++- src/components/ResourcesTable.tsx | 22 +++++++++----------- src/components/RolesDataTable.tsx | 8 +++++++- src/components/RolesTable.tsx | 22 ++++++++++++++++++++ src/components/ShareLinksDataTable.tsx | 8 +++++++- src/components/ShareLinksTable.tsx | 21 +++++++++++++++++++ src/components/UsersDataTable.tsx | 8 +++++++- src/components/UsersTable.tsx | 20 ++++++++++++++++++ 9 files changed, 127 insertions(+), 17 deletions(-) diff --git a/src/components/InvitationsDataTable.tsx b/src/components/InvitationsDataTable.tsx index 396a3c20..d73ad2ca 100644 --- a/src/components/InvitationsDataTable.tsx +++ b/src/components/InvitationsDataTable.tsx @@ -9,11 +9,15 @@ import { useTranslations } from 'next-intl'; interface DataTableProps { columns: ColumnDef[]; data: TData[]; + onRefresh?: () => void; + isRefreshing?: boolean; } export function InvitationsDataTable({ columns, - data + data, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -26,6 +30,8 @@ export function InvitationsDataTable({ title={t('invite')} searchPlaceholder={t('inviteSearch')} searchColumn="email" + onRefresh={onRefresh} + isRefreshing={isRefreshing} /> ); } diff --git a/src/components/InvitationsTable.tsx b/src/components/InvitationsTable.tsx index a97220f2..900003d7 100644 --- a/src/components/InvitationsTable.tsx +++ b/src/components/InvitationsTable.tsx @@ -19,6 +19,7 @@ import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; import moment from "moment"; +import { useRouter } from "next/navigation"; export type InvitationRow = { id: string; @@ -45,6 +46,25 @@ export default function InvitationsTable({ const api = createApiClient(useEnvContext()); const { org } = useOrgContext(); + const router = useRouter(); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; const columns: ColumnDef[] = [ { @@ -185,7 +205,12 @@ export default function InvitationsTable({ }} /> - + ); } diff --git a/src/components/ResourcesTable.tsx b/src/components/ResourcesTable.tsx index ad60c685..ad8b4fab 100644 --- a/src/components/ResourcesTable.tsx +++ b/src/components/ResourcesTable.tsx @@ -773,18 +773,16 @@ export default function ResourcesTable({
- {refreshData && ( - - )} +
{getActionButton()} diff --git a/src/components/RolesDataTable.tsx b/src/components/RolesDataTable.tsx index e88f9a2f..8043fc23 100644 --- a/src/components/RolesDataTable.tsx +++ b/src/components/RolesDataTable.tsx @@ -10,12 +10,16 @@ interface DataTableProps { columns: ColumnDef[]; data: TData[]; createRole?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; } export function RolesDataTable({ columns, data, - createRole + createRole, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -29,6 +33,8 @@ export function RolesDataTable({ searchPlaceholder={t('accessRolesSearch')} searchColumn="name" onAdd={createRole} + onRefresh={onRefresh} + isRefreshing={isRefreshing} addButtonText={t('accessRolesAdd')} /> ); diff --git a/src/components/RolesTable.tsx b/src/components/RolesTable.tsx index e92e71b6..292384a8 100644 --- a/src/components/RolesTable.tsx +++ b/src/components/RolesTable.tsx @@ -20,6 +20,7 @@ import DeleteRoleForm from "@app/components/DeleteRoleForm"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; export type RoleRow = Role; @@ -30,6 +31,7 @@ type RolesTableProps = { export default function UsersTable({ roles: r }: RolesTableProps) { const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const router = useRouter(); const [roles, setRoles] = useState(r); @@ -40,6 +42,24 @@ export default function UsersTable({ roles: r }: RolesTableProps) { const { org } = useOrgContext(); const t = useTranslations(); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; const columns: ColumnDef[] = [ { @@ -116,6 +136,8 @@ export default function UsersTable({ roles: r }: RolesTableProps) { createRole={() => { setIsCreateModalOpen(true); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); diff --git a/src/components/ShareLinksDataTable.tsx b/src/components/ShareLinksDataTable.tsx index dd266bcf..f2753bcf 100644 --- a/src/components/ShareLinksDataTable.tsx +++ b/src/components/ShareLinksDataTable.tsx @@ -10,12 +10,16 @@ interface DataTableProps { columns: ColumnDef[]; data: TData[]; createShareLink?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; } export function ShareLinksDataTable({ columns, data, - createShareLink + createShareLink, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -29,6 +33,8 @@ export function ShareLinksDataTable({ searchPlaceholder={t('shareSearch')} searchColumn="name" onAdd={createShareLink} + onRefresh={onRefresh} + isRefreshing={isRefreshing} addButtonText={t('shareCreate')} /> ); diff --git a/src/components/ShareLinksTable.tsx b/src/components/ShareLinksTable.tsx index 2943311f..ba9169c1 100644 --- a/src/components/ShareLinksTable.tsx +++ b/src/components/ShareLinksTable.tsx @@ -61,6 +61,25 @@ export default function ShareLinksTable({ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [rows, setRows] = useState(shareLinks); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + function formatLink(link: string) { return link.substring(0, 20) + "..." + link.substring(link.length - 20); } @@ -292,6 +311,8 @@ export default function ShareLinksTable({ createShareLink={() => { setIsCreateModalOpen(true); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); diff --git a/src/components/UsersDataTable.tsx b/src/components/UsersDataTable.tsx index 1999b620..db12b697 100644 --- a/src/components/UsersDataTable.tsx +++ b/src/components/UsersDataTable.tsx @@ -10,12 +10,16 @@ interface DataTableProps { columns: ColumnDef[]; data: TData[]; inviteUser?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; } export function UsersDataTable({ columns, data, - inviteUser + inviteUser, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -29,6 +33,8 @@ export function UsersDataTable({ searchPlaceholder={t('accessUsersSearch')} searchColumn="email" onAdd={inviteUser} + onRefresh={onRefresh} + isRefreshing={isRefreshing} addButtonText={t('accessUserCreate')} /> ); diff --git a/src/components/UsersTable.tsx b/src/components/UsersTable.tsx index 2d4c122f..be8aea49 100644 --- a/src/components/UsersTable.tsx +++ b/src/components/UsersTable.tsx @@ -51,6 +51,24 @@ export default function UsersTable({ users: u }: UsersTableProps) { const { user, updateUser } = useUserContext(); const { org } = useOrgContext(); const t = useTranslations(); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; const columns: ColumnDef[] = [ { @@ -290,6 +308,8 @@ export default function UsersTable({ users: u }: UsersTableProps) { `/${org?.org.orgId}/settings/access/users/create` ); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); From cd27f6459c0566737d822853703f1316a3fc52f8 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 5 Oct 2025 12:30:01 +0530 Subject: [PATCH 225/322] refresh button --- src/components/OrgApiKeysDataTable.tsx | 8 +++++++- src/components/OrgApiKeysTable.tsx | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/components/OrgApiKeysDataTable.tsx b/src/components/OrgApiKeysDataTable.tsx index 773b2141..b6ad4bc3 100644 --- a/src/components/OrgApiKeysDataTable.tsx +++ b/src/components/OrgApiKeysDataTable.tsx @@ -8,12 +8,16 @@ interface DataTableProps { columns: ColumnDef[]; data: TData[]; addApiKey?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; } export function OrgApiKeysDataTable({ addApiKey, columns, - data + data, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -27,6 +31,8 @@ export function OrgApiKeysDataTable({ searchPlaceholder={t('searchApiKeys')} searchColumn="name" onAdd={addApiKey} + onRefresh={onRefresh} + isRefreshing={isRefreshing} addButtonText={t('apiKeysAdd')} /> ); diff --git a/src/components/OrgApiKeysTable.tsx b/src/components/OrgApiKeysTable.tsx index 52030b66..d4c81e80 100644 --- a/src/components/OrgApiKeysTable.tsx +++ b/src/components/OrgApiKeysTable.tsx @@ -46,6 +46,24 @@ export default function OrgApiKeysTable({ const api = createApiClient(useEnvContext()); const t = useTranslations(); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; const deleteSite = (apiKeyId: string) => { api.delete(`/org/${orgId}/api-key/${apiKeyId}`) @@ -195,6 +213,8 @@ export default function OrgApiKeysTable({ addApiKey={() => { router.push(`/${orgId}/settings/api-keys/create`); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); From c7c3e3ee7393f3445a07262c9a63651bcd871c96 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Sun, 5 Oct 2025 23:19:35 +0530 Subject: [PATCH 226/322] refresh button inside admin --- src/components/AdminIdpDataTable.tsx | 8 +++++++- src/components/AdminIdpTable.tsx | 25 ++++++++++++++++++++++- src/components/AdminUsersDataTable.tsx | 8 +++++++- src/components/AdminUsersTable.tsx | 28 ++++++++++++++++++++++++-- src/components/ApiKeysDataTable.tsx | 10 +++++++-- src/components/ApiKeysTable.tsx | 21 +++++++++++++++++++ 6 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/components/AdminIdpDataTable.tsx b/src/components/AdminIdpDataTable.tsx index 2efd9e7c..63a0b4bb 100644 --- a/src/components/AdminIdpDataTable.tsx +++ b/src/components/AdminIdpDataTable.tsx @@ -8,11 +8,15 @@ import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; data: TData[]; + onRefresh?: () => void; + isRefreshing?: boolean; } export function IdpDataTable({ columns, - data + data, + onRefresh, + isRefreshing }: DataTableProps) { const router = useRouter(); const t = useTranslations(); @@ -29,6 +33,8 @@ export function IdpDataTable({ onAdd={() => { router.push("/admin/idp/create"); }} + onRefresh={onRefresh} + isRefreshing={isRefreshing} /> ); } diff --git a/src/components/AdminIdpTable.tsx b/src/components/AdminIdpTable.tsx index 8849ba25..2db1415e 100644 --- a/src/components/AdminIdpTable.tsx +++ b/src/components/AdminIdpTable.tsx @@ -39,8 +39,26 @@ export default function IdpTable({ idps }: Props) { const [selectedIdp, setSelectedIdp] = useState(null); const api = createApiClient(useEnvContext()); const router = useRouter(); + const [isRefreshing, setIsRefreshing] = useState(false); const t = useTranslations(); + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + const deleteIdp = async (idpId: number) => { try { await api.delete(`/idp/${idpId}`); @@ -194,7 +212,12 @@ export default function IdpTable({ idps }: Props) { /> )} - + ); } diff --git a/src/components/AdminUsersDataTable.tsx b/src/components/AdminUsersDataTable.tsx index fecba7fb..b0f38587 100644 --- a/src/components/AdminUsersDataTable.tsx +++ b/src/components/AdminUsersDataTable.tsx @@ -9,11 +9,15 @@ import { useTranslations } from "next-intl"; interface DataTableProps { columns: ColumnDef[]; data: TData[]; + onRefresh?: () => void; + isRefreshing?: boolean; } export function UsersDataTable({ columns, - data + data, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); @@ -26,6 +30,8 @@ export function UsersDataTable({ title={t('userServer')} searchPlaceholder={t('userSearch')} searchColumn="email" + onRefresh={onRefresh} + isRefreshing={isRefreshing} /> ); } diff --git a/src/components/AdminUsersTable.tsx b/src/components/AdminUsersTable.tsx index 8e75ff24..6bca4a74 100644 --- a/src/components/AdminUsersTable.tsx +++ b/src/components/AdminUsersTable.tsx @@ -46,6 +46,25 @@ export default function UsersTable({ users }: Props) { const api = createApiClient(useEnvContext()); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + const deleteUser = (id: string) => { api.delete(`/user/${id}`) .catch((e) => { @@ -168,7 +187,7 @@ export default function UsersTable({ users }: Props) {
{userRow.twoFactorEnabled || - userRow.twoFactorSetupRequested ? ( + userRow.twoFactorSetupRequested ? ( {t("enabled")} @@ -263,7 +282,12 @@ export default function UsersTable({ users }: Props) { /> )} - + ); } diff --git a/src/components/ApiKeysDataTable.tsx b/src/components/ApiKeysDataTable.tsx index 6ac8d68b..58ab9252 100644 --- a/src/components/ApiKeysDataTable.tsx +++ b/src/components/ApiKeysDataTable.tsx @@ -33,16 +33,20 @@ interface DataTableProps { columns: ColumnDef[]; data: TData[]; addApiKey?: () => void; + onRefresh?: () => void; + isRefreshing?: boolean; } export function ApiKeysDataTable({ addApiKey, columns, - data + data, + onRefresh, + isRefreshing }: DataTableProps) { const t = useTranslations(); - + return ( ({ searchColumn="name" onAdd={addApiKey} addButtonText={t('apiKeysAdd')} + onRefresh={onRefresh} + isRefreshing={isRefreshing} /> ); } diff --git a/src/components/ApiKeysTable.tsx b/src/components/ApiKeysTable.tsx index 99094651..adc150cf 100644 --- a/src/components/ApiKeysTable.tsx +++ b/src/components/ApiKeysTable.tsx @@ -43,6 +43,25 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { const t = useTranslations(); + const [isRefreshing, setIsRefreshing] = useState(false); + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + await new Promise((resolve) => setTimeout(resolve, 200)); + router.refresh(); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + const deleteSite = (apiKeyId: string) => { api.delete(`/api-key/${apiKeyId}`) .catch((e) => { @@ -186,6 +205,8 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) { addApiKey={() => { router.push(`/admin/api-keys/create`); }} + onRefresh={refreshData} + isRefreshing={isRefreshing} /> ); From ca146a1b5788a6318c69fcb2f58275c221124009 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 6 Oct 2025 00:50:38 +0530 Subject: [PATCH 227/322] adjust target config column --- messages/en-US.json | 2 + .../resources/[niceId]/proxy/page.tsx | 270 +++++++++--------- src/components/TargetDisplay.tsx | 44 +++ src/components/TargetModal.tsx | 144 ++++++++++ 4 files changed, 322 insertions(+), 138 deletions(-) create mode 100644 src/components/TargetDisplay.tsx create mode 100644 src/components/TargetModal.tsx diff --git a/messages/en-US.json b/messages/en-US.json index f725f853..3cbc4a04 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -469,6 +469,8 @@ "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", "proxyEnableSSL": "Enable SSL (https)", + "target": "Target", + "configureTargets": "Configure Targets", "targetErrorFetch": "Failed to fetch targets", "targetErrorFetchDescription": "An error occurred while fetching targets", "siteErrorFetch": "Failed to fetch resource", diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 302d16d2..028c97c7 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -110,6 +110,8 @@ import { } from "@app/components/PathMatchRenameModal"; import { Badge } from "@app/components/ui/badge"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; +import { TargetModal } from "@app/components/TargetModal"; +import { TargetDisplay } from "@app/components/TargetDisplay"; const addTargetSchema = z .object({ @@ -537,11 +539,11 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...data, - updated: true, - siteType: site?.type || null - } + ...target, + ...data, + updated: true, + siteType: site?.type || null + } : target ) ); @@ -552,10 +554,10 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...config, - updated: true - } + ...target, + ...config, + updated: true + } : target ) ); @@ -743,9 +745,9 @@ export default function ReverseProxyTargets(props: { } /> - {/* */} +
) : ( +
+ } + /> + + +
+ ) : ( + updateTarget(row.original.targetId, { ...row.original, - ip: input - }); + ...config + }) } - }} - /> - ) - }, - { - accessorKey: "port", - header: t("targetPort"), - cell: ({ row }) => ( - - updateTarget(row.original.targetId, { - ...row.original, - port: parseInt(e.target.value, 10) - }) - } - /> - ) + showMethod={resource.http} + trigger={ + + } + /> + ); + } }, { accessorKey: "rewritePath", @@ -990,7 +985,6 @@ export default function ReverseProxyTargets(props: { return hasRewritePath && !noPathMatch ? (
- {/* */} @@ -1318,34 +1312,34 @@ export default function ReverseProxyTargets(props: { ); return selectedSite && selectedSite.type === - "newt" + "newt" ? (() => { - const dockerState = - getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })() + const dockerState = + getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })() : null; })()}
@@ -1558,7 +1552,7 @@ export default function ReverseProxyTargets(props: { -
+
{table @@ -1573,12 +1567,12 @@ export default function ReverseProxyTargets(props: { {header.isPlaceholder ? null : flexRender( - header - .column - .columnDef - .header, - header.getContext() - )} + header + .column + .columnDef + .header, + header.getContext() + )} ) )} diff --git a/src/components/TargetDisplay.tsx b/src/components/TargetDisplay.tsx new file mode 100644 index 00000000..5ca0a9a7 --- /dev/null +++ b/src/components/TargetDisplay.tsx @@ -0,0 +1,44 @@ +import { Globe, Hash, Shield } from "lucide-react"; + +interface TargetDisplayProps { + value: { + method?: string | null; + ip?: string; + port?: number; + }; + showMethod?: boolean; +} + +export function TargetDisplay({ value, showMethod = true }: TargetDisplayProps) { + const { method, ip, port } = value; + + if (!ip && !port && !method) { + return Not configured; + } + + + + return ( +
+ {showMethod && method && ( + + {method === "https" && } + + {method}:// + + + )} + {ip && ( + + {ip} + {port && :} + + )} + {port && ( + + {port} + + )} +
+ ); +} diff --git a/src/components/TargetModal.tsx b/src/components/TargetModal.tsx new file mode 100644 index 00000000..8a5ec336 --- /dev/null +++ b/src/components/TargetModal.tsx @@ -0,0 +1,144 @@ + +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useState } from "react"; + +interface TargetConfig { + method?: string | null; + ip?: string; + port?: number; +} + +interface TargetModalProps { + value: TargetConfig; + onChange: (config: TargetConfig) => void; + trigger: React.ReactNode; + showMethod?: boolean; +} + +export function TargetModal({ + value, + onChange, + trigger, + showMethod = true +}: TargetModalProps) { + const [open, setOpen] = useState(false); + const [config, setConfig] = useState(value); + + const handleSave = () => { + onChange(config); + setOpen(false); + }; + + const parseHostTarget = (input: string) => { + const protocolMatch = input.match(/^(https?|h2c):\/\//); + const protocol = protocolMatch ? protocolMatch[1] : null; + const withoutProtocol = input.replace(/^(https?|h2c):\/\//, ''); + + const portMatch = withoutProtocol.match(/:(\d+)(?:\/|$)/); + const port = portMatch ? parseInt(portMatch[1], 10) : null; + const host = withoutProtocol.replace(/:\d+(?:\/|$)/, '').replace(/\/$/, ''); + + return { protocol, host, port }; + }; + + const handleHostChange = (input: string) => { + const trimmed = input.trim(); + const hasProtocol = /^(https?|h2c):\/\//.test(trimmed); + const hasPort = /:\d+(?:\/|$)/.test(trimmed); + + if (hasProtocol || hasPort) { + const parsed = parseHostTarget(trimmed); + setConfig({ + ...config, + ...(hasProtocol && parsed.protocol ? { method: parsed.protocol } : {}), + ip: parsed.host, + ...(hasPort && parsed.port ? { port: parsed.port } : {}) + }); + } else { + setConfig({ ...config, ip: trimmed }); + } + }; + + return ( + + {trigger} + + + Configure Target + +
+ {showMethod && ( +
+ + +
+ )} +
+ + setConfig({ ...config, ip: e.target.value })} + onBlur={(e) => handleHostChange(e.target.value)} + /> +

+ You can also paste: http://example.com:8080 +

+
+
+ + + setConfig({ + ...config, + port: parseInt(e.target.value, 10) || undefined + }) + } + /> +
+
+
+ + +
+
+
+ ); +} \ No newline at end of file From d20e0a228a6ab8f07d14d1409e8a35ce175ea229 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Mon, 6 Oct 2025 01:06:43 +0530 Subject: [PATCH 228/322] adjust target config ui inside create resource --- messages/en-US.json | 2 +- .../resources/[niceId]/proxy/page.tsx | 2 +- .../settings/resources/create/page.tsx | 145 +++++++++--------- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 3cbc4a04..b58f1493 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -470,7 +470,7 @@ "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", "proxyEnableSSL": "Enable SSL (https)", "target": "Target", - "configureTargets": "Configure Targets", + "configureTarget": "Configure Targets", "targetErrorFetch": "Failed to fetch targets", "targetErrorFetchDescription": "An error occurred while fetching targets", "siteErrorFetch": "Failed to fetch resource", diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 028c97c7..eae1d116 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -918,7 +918,7 @@ export default function ReverseProxyTargets(props: { trigger={ } - } else { + /> + + + + ) : ( + updateTarget(row.original.targetId, { ...row.original, - ip: input - }); + ...config + }) } - }} - /> - ) - }, - { - accessorKey: "port", - header: t("targetPort"), - cell: ({ row }) => ( - - updateTarget(row.original.targetId, { - ...row.original, - port: parseInt(e.target.value, 10) - }) - } - /> - ) + showMethod={baseForm.watch("http")} + trigger={ + + } + /> + ); + } }, { accessorKey: "rewritePath", From 0a377150e342304bb5d66be594fef29a828d6acd Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Wed, 8 Oct 2025 22:35:40 +0530 Subject: [PATCH 229/322] reorder columns --- .../resources/[niceId]/proxy/page.tsx | 260 ++++++++---------- 1 file changed, 109 insertions(+), 151 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index eae1d116..1f7a3f3d 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -671,6 +671,21 @@ export default function ReverseProxyTargets(props: { } const columns: ColumnDef[] = [ + { + accessorKey: "enabled", + header: t("enabled"), + cell: ({ row }) => ( + + updateTarget(row.original.targetId, { + ...row.original, + enabled: val + }) + } + /> + ) + }, { id: "priority", header: () => ( @@ -711,6 +726,82 @@ export default function ReverseProxyTargets(props: { ); } }, + { + accessorKey: "healthCheck", + header: t("healthCheck"), + cell: ({ row }) => { + const status = row.original.hcHealth || "unknown"; + const isEnabled = row.original.hcEnabled; + + const getStatusColor = (status: string) => { + switch (status) { + case "healthy": + return "green"; + case "unhealthy": + return "red"; + case "unknown": + default: + return "secondary"; + } + }; + + const getStatusText = (status: string) => { + switch (status) { + case "healthy": + return t("healthCheckHealthy"); + case "unhealthy": + return t("healthCheckUnhealthy"); + case "unknown": + default: + return t("healthCheckUnknown"); + } + }; + + const getStatusIcon = (status: string) => { + switch (status) { + case "healthy": + return ; + case "unhealthy": + return ; + case "unknown": + default: + return null; + } + }; + + return ( + <> + {row.original.siteType === "newt" ? ( + + + + ) : ( + + {t("healthCheckNotAvailable")} + + )} + + ); + } + }, { accessorKey: "path", header: t("matchPath"), @@ -744,42 +835,27 @@ export default function ReverseProxyTargets(props: { } /> - - ) : ( - - updateTarget(row.original.targetId, config) - } - trigger={ - - } - /> +
+ + updateTarget(row.original.targetId, config) + } + trigger={ + + } + /> + +
); } }, @@ -931,22 +1007,6 @@ export default function ReverseProxyTargets(props: { } /> - ) : ( @@ -1010,21 +1070,6 @@ export default function ReverseProxyTargets(props: { } /> - ) : ( // ), // }, - { - accessorKey: "healthCheck", - header: t("healthCheck"), - cell: ({ row }) => { - const status = row.original.hcHealth || "unknown"; - const isEnabled = row.original.hcEnabled; - const getStatusColor = (status: string) => { - switch (status) { - case "healthy": - return "green"; - case "unhealthy": - return "red"; - case "unknown": - default: - return "secondary"; - } - }; - - const getStatusText = (status: string) => { - switch (status) { - case "healthy": - return t("healthCheckHealthy"); - case "unhealthy": - return t("healthCheckUnhealthy"); - case "unknown": - default: - return t("healthCheckUnknown"); - } - }; - - const getStatusIcon = (status: string) => { - switch (status) { - case "healthy": - return ; - case "unhealthy": - return ; - case "unknown": - default: - return null; - } - }; - - return ( - <> - {row.original.siteType === "newt" ? ( -
- -
- {getStatusIcon(status)} - {getStatusText(status)} -
-
- -
- ) : ( - - {t("healthCheckNotAvailable")} - - )} - - ); - } - }, - { - accessorKey: "enabled", - header: t("enabled"), - cell: ({ row }) => ( - - updateTarget(row.original.targetId, { - ...row.original, - enabled: val - }) - } - /> - ) - }, { id: "actions", cell: ({ row }) => ( From a6086d3724d233b5c81acd13e8733fba619a1d77 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Thu, 9 Oct 2025 00:53:45 +0530 Subject: [PATCH 230/322] address input design --- .../resources/[niceId]/proxy/page.tsx | 255 +++++++++--------- 1 file changed, 126 insertions(+), 129 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 1f7a3f3d..fdb7ff91 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -860,17 +860,14 @@ export default function ReverseProxyTargets(props: { } }, { - accessorKey: "siteId", - header: t("site"), + accessorKey: "address", + header: t("address"), cell: ({ row }) => { const selectedSite = sites.find( (site) => site.siteId === row.original.siteId ); - const handleContainerSelectForTarget = ( - hostname: string, - port?: number - ) => { + const handleContainerSelectForTarget = (hostname: string, port?: number) => { updateTarget(row.original.targetId, { ...row.original, ip: hostname @@ -884,67 +881,53 @@ export default function ReverseProxyTargets(props: { }; return ( -
- - - - - - - - - - {t("siteNotFound")} - - - {sites.map((site) => ( - { - updateTarget( - row.original - .targetId, - { - siteId: site.siteId - } - ); - }} - > - - {site.name} - - ))} - - - - - - {selectedSite && +
+ + + + + + + {t("siteNotFound")} + + {sites.map((site) => ( + + updateTarget(row.original.targetId, { siteId: site.siteId }) + } + > + + {site.name} + + ))} + + + + + + {selectedSite && selectedSite.type === "newt" && (() => { const dockerState = getDockerStateForSite( @@ -966,70 +949,84 @@ export default function ReverseProxyTargets(props: { /> ); })()} -
- ); - } - }, - { - accessorKey: "target", - header: t("target"), - cell: ({ row }) => { - const hasTarget = !!(row.original.ip || row.original.port || row.original.method); - return hasTarget ? ( -
- - updateTarget(row.original.targetId, { - ...row.original, - ...config - }) - } - showMethod={resource.http} - trigger={ - - } - /> - + + +
+ {"://"} +
+ + { + const input = e.target.value.trim(); + const hasProtocol = /^(https?|h2c):\/\//.test(input); + const hasPort = /:\d+(?:\/|$)/.test(input); + + if (hasProtocol || hasPort) { + const parsed = parseHostTarget(input); + if (parsed) { + updateTarget(row.original.targetId, { + ...row.original, + method: hasProtocol + ? parsed.protocol + : row.original.method, + ip: parsed.host, + port: hasPort + ? parsed.port + : row.original.port + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + ip: input + }); + } + } else { + updateTarget(row.original.targetId, { + ...row.original, + ip: input + }); + } + }} + /> +
+ {":"} +
+ + updateTarget(row.original.targetId, { + ...row.original, + port: parseInt(e.target.value, 10) + }) + } + /> + +
- ) : ( - - updateTarget(row.original.targetId, { - ...row.original, - ...config - }) - } - showMethod={resource.http} - trigger={ - - } - /> ); } }, From 94137e587c66f04a5d146ef7aea7aa010f00f642 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Thu, 9 Oct 2025 01:33:42 +0530 Subject: [PATCH 231/322] change target config ui for create resource --- .../resources/[niceId]/proxy/page.tsx | 5 +- .../settings/resources/create/page.tsx | 423 ++++++++---------- 2 files changed, 198 insertions(+), 230 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index fdb7ff91..40cf217d 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -969,7 +969,7 @@ export default function ReverseProxyTargets(props: { -
+
{"://"}
@@ -1009,11 +1009,10 @@ export default function ReverseProxyTargets(props: { } }} /> -
+
{":"}
[] = [ + { + accessorKey: "enabled", + header: t("enabled"), + cell: ({ row }) => ( + + updateTarget(row.original.targetId, { + ...row.original, + enabled: val + }) + } + /> + ) + }, { id: "priority", header: () => ( @@ -674,55 +690,37 @@ export default function Page() { } /> - - - {/* */} +
) : ( - + updateTarget(row.original.targetId, config)} - trigger={ - - } - /> + trigger={ + + } + /> + +
); - }, + } }, { - accessorKey: "siteId", - header: t("site"), + accessorKey: "address", + header: t("address"), cell: ({ row }) => { const selectedSite = sites.find( (site) => site.siteId === row.original.siteId ); - const handleContainerSelectForTarget = ( - hostname: string, - port?: number - ) => { + const handleContainerSelectForTarget = (hostname: string, port?: number) => { updateTarget(row.original.targetId, { ...row.original, ip: hostname @@ -736,158 +734,151 @@ export default function Page() { }; return ( -
- - - - - - - - - - {t("siteNotFound")} - - - {sites.map((site) => ( - { - updateTarget( - row.original - .targetId, - { - siteId: site.siteId - } - ); - }} - > - - {site.name} - - ))} - - - - - - {selectedSite && selectedSite.type === "newt" && (() => { - const dockerState = getDockerStateForSite(selectedSite.siteId); - return ( - refreshContainersForSite(selectedSite.siteId)} - /> - ); - })()} -
- ); - } - }, - { - accessorKey: "target", - header: t("target"), - cell: ({ row }) => { - const hasTarget = !!(row.original.ip || row.original.port || row.original.method); - - return hasTarget ? (
- - updateTarget(row.original.targetId, { - ...row.original, - ...config - }) - } - showMethod={baseForm.watch("http")} - trigger={ - - } - /> - + + + + + + {t("siteNotFound")} + + {sites.map((site) => ( + + updateTarget(row.original.targetId, { siteId: site.siteId }) + } + > + + {site.name} + + ))} + + + + + + {selectedSite && + selectedSite.type === "newt" && + (() => { + const dockerState = getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })()} + + + +
+ {"://"} +
+ + { + const input = e.target.value.trim(); + const hasProtocol = /^(https?|h2c):\/\//.test(input); + const hasPort = /:\d+(?:\/|$)/.test(input); + + if (hasProtocol || hasPort) { + const parsed = parseHostTarget(input); + if (parsed) { + updateTarget(row.original.targetId, { + ...row.original, + method: hasProtocol + ? parsed.protocol + : row.original.method, + ip: parsed.host, + port: hasPort + ? parsed.port + : row.original.port + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + ip: input + }); + } + } else { + updateTarget(row.original.targetId, { + ...row.original, + ip: input + }); + } + }} + /> +
+ {":"} +
+ + updateTarget(row.original.targetId, { + ...row.original, + port: parseInt(e.target.value, 10) + }) + } + /> - +
- ) : ( - - updateTarget(row.original.targetId, { - ...row.original, - ...config - }) - } - showMethod={baseForm.watch("http")} - trigger={ - - } - /> ); } }, @@ -895,18 +886,22 @@ export default function Page() { accessorKey: "rewritePath", header: t("rewritePath"), cell: ({ row }) => { - const hasRewritePath = !!(row.original.rewritePath || row.original.rewritePathType); - const noPathMatch = !row.original.path && !row.original.pathMatchType; + const hasRewritePath = !!( + row.original.rewritePath || row.original.rewritePathType + ); + const noPathMatch = + !row.original.path && !row.original.pathMatchType; return hasRewritePath && !noPathMatch ? (
- {/* */} updateTarget(row.original.targetId, config)} + onChange={(config) => + updateTarget(row.original.targetId, config) + } trigger={ } /> -
) : ( updateTarget(row.original.targetId, config)} + onChange={(config) => + updateTarget(row.original.targetId, config) + } trigger={ - -
- - - ); -} \ No newline at end of file From c0cc81ed9643ed157186673eee2b162793a858f0 Mon Sep 17 00:00:00 2001 From: Pallavi Kumari Date: Fri, 10 Oct 2025 23:25:33 +0530 Subject: [PATCH 233/322] standardizing the targets input table --- server/routers/resource/createResource.ts | 7 +- .../settings/resources/create/page.tsx | 216 +++++++++++++++++- 2 files changed, 217 insertions(+), 6 deletions(-) diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index f4fe352e..2a4e67a7 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -37,7 +37,8 @@ const createHttpResourceSchema = z subdomain: z.string().nullable().optional(), http: z.boolean(), protocol: z.enum(["tcp", "udp"]), - domainId: z.string() + domainId: z.string(), + stickySession: z.boolean().optional(), }) .strict() .refine( @@ -191,6 +192,7 @@ async function createHttpResource( const { name, domainId } = parsedBody.data; const subdomain = parsedBody.data.subdomain; + const stickySession=parsedBody.data.stickySession; // Validate domain and construct full domain const domainResult = await validateAndConstructDomain( @@ -254,7 +256,8 @@ async function createHttpResource( subdomain: finalSubdomain, http: true, protocol: "tcp", - ssl: true + ssl: true, + stickySession: stickySession }) .returning(); diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 10828275..1036538d 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -58,7 +58,7 @@ import { } from "@app/components/ui/popover"; import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"; import { cn } from "@app/lib/cn"; -import { ArrowRight, CircleCheck, CircleX, Info, MoveRight, Plus, SquareArrowOutUpRight } from "lucide-react"; +import { ArrowRight, CircleCheck, CircleX, Info, MoveRight, Plus, Settings, SquareArrowOutUpRight } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import Link from "next/link"; import { useTranslations } from "next-intl"; @@ -95,6 +95,8 @@ import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; import { PathMatchDisplay, PathMatchModal, PathRewriteDisplay, PathRewriteModal } from "@app/components/PathMatchRenameModal"; import { Badge } from "@app/components/ui/badge"; +import HealthCheckDialog from "@app/components/HealthCheckDialog"; +import { SwitchInput } from "@app/components/SwitchInput"; const baseResourceFormSchema = z.object({ @@ -113,6 +115,11 @@ const tcpUdpResourceFormSchema = z.object({ // enableProxy: z.boolean().default(false) }); +const targetsSettingsSchema = z.object({ + stickySession: z.boolean() +}); + + const addTargetSchema = z.object({ ip: z.string().refine(isTargetValid), method: z.string().nullable(), @@ -216,6 +223,10 @@ export default function Page() { const [targetsToRemove, setTargetsToRemove] = useState([]); const [dockerStates, setDockerStates] = useState>(new Map()); + const [selectedTargetForHealthCheck, setSelectedTargetForHealthCheck] = + useState(null); + const [healthCheckDialogOpen, setHealthCheckDialogOpen] = useState(false); + const resourceTypes: ReadonlyArray = [ { id: "http", @@ -269,6 +280,13 @@ export default function Page() { } as z.infer }); + const targetsSettingsForm = useForm({ + resolver: zodResolver(targetsSettingsSchema), + defaultValues: { + stickySession: false + } + }); + const watchedIp = addTargetForm.watch("ip"); const watchedPort = addTargetForm.watch("port"); const watchedSiteId = addTargetForm.watch("siteId"); @@ -406,11 +424,13 @@ export default function Page() { const baseData = baseForm.getValues(); const isHttp = baseData.http; + const stickySessionData = targetsSettingsForm.getValues() try { const payload = { name: baseData.name, - http: baseData.http + http: baseData.http, + stickySession: stickySessionData.stickySession }; let sanitizedSubdomain: string | undefined; @@ -604,6 +624,26 @@ export default function Page() { load(); }, []); + function TargetHealthCheck(targetId: number, config: any) { + setTargets( + targets.map((target) => + target.targetId === targetId + ? { + ...target, + ...config, + updated: true + } + : target + ) + ); + } + + const openHealthCheckDialog = (target: LocalTarget) => { + console.log(target); + setSelectedTargetForHealthCheck(target); + setHealthCheckDialogOpen(true); + }; + const columns: ColumnDef[] = [ { accessorKey: "enabled", @@ -660,6 +700,82 @@ export default function Page() { ); } }, + { + accessorKey: "healthCheck", + header: t("healthCheck"), + cell: ({ row }) => { + const status = row.original.hcHealth || "unknown"; + const isEnabled = row.original.hcEnabled; + + const getStatusColor = (status: string) => { + switch (status) { + case "healthy": + return "green"; + case "unhealthy": + return "red"; + case "unknown": + default: + return "secondary"; + } + }; + + const getStatusText = (status: string) => { + switch (status) { + case "healthy": + return t("healthCheckHealthy"); + case "unhealthy": + return t("healthCheckUnhealthy"); + case "unknown": + default: + return t("healthCheckUnknown"); + } + }; + + const getStatusIcon = (status: string) => { + switch (status) { + case "healthy": + return ; + case "unhealthy": + return ; + case "unknown": + default: + return null; + } + }; + + return ( + <> + {row.original.siteType === "newt" ? ( + + + + ) : ( + + {t("healthCheckNotAvailable")} + + )} + + ); + } + }, { accessorKey: "path", header: t("matchPath"), @@ -695,9 +811,9 @@ export default function Page() { updateTarget(row.original.targetId, config)} + onChange={(config) => updateTarget(row.original.targetId, config)} trigger={
@@ -1679,6 +1838,55 @@ export default function Page() { {t("resourceCreate")} + {selectedTargetForHealthCheck && ( + { + if (selectedTargetForHealthCheck) { + console.log(config); + TargetHealthCheck( + selectedTargetForHealthCheck.targetId, + config + ); + } + }} + /> + )} ) : ( From 2f5e6248cda5614400ff084c6a30eb5a74b578c8 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 8 Oct 2025 16:42:30 -0700 Subject: [PATCH 234/322] Small ui adjustments --- .../resources/[niceId]/proxy/page.tsx | 274 ++++++++++-------- src/components/PathMatchRenameModal.tsx | 4 +- 2 files changed, 161 insertions(+), 117 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index 4b6b358b..ab434b32 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -109,7 +109,12 @@ import { PathRewriteModal } from "@app/components/PathMatchRenameModal"; import { Badge } from "@app/components/ui/badge"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@app/components/ui/tooltip"; const addTargetSchema = z .object({ @@ -517,7 +522,7 @@ export default function ReverseProxyTargets(props: { pathMatchType: null, rewritePath: null, rewritePathType: null, - priority: 100, + priority: 100 }); } @@ -537,11 +542,11 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...data, - updated: true, - siteType: site?.type || null - } + ...target, + ...data, + updated: true, + siteType: site?.type || null + } : target ) ); @@ -552,10 +557,10 @@ export default function ReverseProxyTargets(props: { targets.map((target) => target.targetId === targetId ? { - ...target, - ...config, - updated: true - } + ...target, + ...config, + updated: true + } : target ) ); @@ -695,7 +700,12 @@ export default function ReverseProxyTargets(props: { -

Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.

+

+ Higher priority routes are evaluated first. + Priority = 100 means automatic ordering + (system decides). Use another number to + enforce manual priority. +

@@ -770,8 +780,13 @@ export default function ReverseProxyTargets(props: { return ( <> {row.original.siteType === "newt" ? ( - ) : ( - - {t("healthCheckNotAvailable")} - + )} ); @@ -865,7 +879,10 @@ export default function ReverseProxyTargets(props: { (site) => site.siteId === row.original.siteId ); - const handleContainerSelectForTarget = (hostname: string, port?: number) => { + const handleContainerSelectForTarget = ( + hostname: string, + port?: number + ) => { updateTarget(row.original.targetId, { ...row.original, ip: hostname @@ -880,7 +897,10 @@ export default function ReverseProxyTargets(props: { return (
- - + - {t("siteNotFound")} + + {t("siteNotFound")} + {sites.map((site) => ( - updateTarget(row.original.targetId, { siteId: site.siteId }) + updateTarget( + row.original + .targetId, + { + siteId: site.siteId + } + ) } > {selectedSite && - selectedSite.type === "newt" && - (() => { - const dockerState = getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })()} + selectedSite.type === "newt" && + (() => { + const dockerState = getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })()} updateTarget(row.original.targetId, { ...row.original, @@ -1184,21 +1228,21 @@ export default function ReverseProxyTargets(props: { className={cn( "justify-between flex-1", !field.value && - "text-muted-foreground" + "text-muted-foreground" )} > {field.value ? sites.find( - ( - site - ) => - site.siteId === - field.value - ) - ?.name + ( + site + ) => + site.siteId === + field.value + ) + ?.name : t( - "siteSelect" - )} + "siteSelect" + )} @@ -1264,34 +1308,34 @@ export default function ReverseProxyTargets(props: { ); return selectedSite && selectedSite.type === - "newt" + "newt" ? (() => { - const dockerState = - getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })() + const dockerState = + getDockerStateForSite( + selectedSite.siteId + ); + return ( + + refreshContainersForSite( + selectedSite.siteId + ) + } + /> + ); + })() : null; })()}
@@ -1519,12 +1563,12 @@ export default function ReverseProxyTargets(props: { {header.isPlaceholder ? null : flexRender( - header - .column - .columnDef - .header, - header.getContext() - )} + header + .column + .columnDef + .header, + header.getContext() + )} ) )} diff --git a/src/components/PathMatchRenameModal.tsx b/src/components/PathMatchRenameModal.tsx index 574c9c70..f67f44c3 100644 --- a/src/components/PathMatchRenameModal.tsx +++ b/src/components/PathMatchRenameModal.tsx @@ -250,7 +250,7 @@ export function PathMatchDisplay({ return (
- + {getTypeLabel(value.pathMatchType)} @@ -281,7 +281,7 @@ export function PathRewriteDisplay({ return (
- + {getTypeLabel(value.rewritePathType)} From 24d564b79bcf8e90a11d7742ad5da18061250c61 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Thu, 9 Oct 2025 18:24:09 -0700 Subject: [PATCH 235/322] add advanced toggle to targets table --- messages/en-US.json | 20 +- .../resources/[niceId]/proxy/page.tsx | 1088 ++++++++--------- src/components/HealthCheckDialog.tsx | 24 +- src/components/PathMatchRenameModal.tsx | 6 +- src/components/ui/textarea.tsx | 2 +- 5 files changed, 525 insertions(+), 615 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index b58f1493..7189af3c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -468,7 +468,8 @@ "createdAt": "Created At", "proxyErrorInvalidHeader": "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header.", "proxyErrorTls": "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name.", - "proxyEnableSSL": "Enable SSL (https)", + "proxyEnableSSL": "Enable SSL", + "proxyEnableSSLDescription": "Enable SSL/TLS encryption for secure HTTPS connections to your targets.", "target": "Target", "configureTarget": "Configure Targets", "targetErrorFetch": "Failed to fetch targets", @@ -497,7 +498,7 @@ "targetTlsSettings": "Secure Connection Configuration", "targetTlsSettingsDescription": "Configure SSL/TLS settings for your resource", "targetTlsSettingsAdvanced": "Advanced TLS Settings", - "targetTlsSni": "TLS Server Name (SNI)", + "targetTlsSni": "TLS Server Name", "targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.", "targetTlsSubmit": "Save Settings", "targets": "Targets Configuration", @@ -506,9 +507,21 @@ "targetStickySessionsDescription": "Keep connections on the same backend target for their entire session.", "methodSelect": "Select method", "targetSubmit": "Add Target", - "targetNoOne": "No targets. Add a target using the form.", + "targetNoOne": "This resource doesn't have any targets. Add a target to configure where to send requests to your backend.", "targetNoOneDescription": "Adding more than one target above will enable load balancing.", "targetsSubmit": "Save Targets", + "addTarget": "Add Target", + "targetErrorInvalidIp": "Invalid IP address", + "targetErrorInvalidIpDescription": "Please enter a valid IP address or hostname", + "targetErrorInvalidPort": "Invalid port", + "targetErrorInvalidPortDescription": "Please enter a valid port number", + "targetErrorNoSite": "No site selected", + "targetErrorNoSiteDescription": "Please select a site for the target", + "targetCreated": "Target created", + "targetCreatedDescription": "Target has been created successfully", + "targetErrorCreate": "Failed to create target", + "targetErrorCreateDescription": "An error occurred while creating the target", + "save": "Save", "proxyAdditional": "Additional Proxy Settings", "proxyAdditionalDescription": "Configure how your resource handles proxy settings", "proxyCustomHeader": "Custom Host Header", @@ -1412,6 +1425,7 @@ "externalProxyEnabled": "External Proxy Enabled", "addNewTarget": "Add New Target", "targetsList": "Targets List", + "advancedMode": "Advanced Mode", "targetErrorDuplicateTargetFound": "Duplicate target found", "healthCheckHealthy": "Healthy", "healthCheckUnhealthy": "Unhealthy", diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index ab434b32..92e02aa2 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -121,7 +121,10 @@ const addTargetSchema = z ip: z.string().refine(isTargetValid), method: z.string().nullable(), port: z.coerce.number().int().positive(), - siteId: z.number().int().positive(), + siteId: z + .number() + .int() + .positive({ message: "You must select a site for a target." }), path: z.string().optional().nullable(), pathMatchType: z .enum(["exact", "prefix", "regex"]) @@ -255,6 +258,13 @@ export default function ReverseProxyTargets(props: { const [pageLoading, setPageLoading] = useState(true); const [isAdvancedOpen, setIsAdvancedOpen] = useState(false); + const [isAdvancedMode, setIsAdvancedMode] = useState(() => { + if (typeof window !== "undefined") { + const saved = localStorage.getItem("proxy-advanced-mode"); + return saved === "true"; + } + return false; + }); const [healthCheckDialogOpen, setHealthCheckDialogOpen] = useState(false); const [selectedTargetForHealthCheck, setSelectedTargetForHealthCheck] = useState(null); @@ -302,31 +312,6 @@ export default function ReverseProxyTargets(props: { type TlsSettingsValues = z.infer; type TargetsSettingsValues = z.infer; - const addTargetForm = useForm({ - resolver: zodResolver(addTargetSchema), - defaultValues: { - ip: "", - method: resource.http ? "http" : null, - port: "" as any as number, - path: null, - pathMatchType: null, - rewritePath: null, - rewritePathType: null, - priority: 100 - } as z.infer - }); - - const watchedIp = addTargetForm.watch("ip"); - const watchedPort = addTargetForm.watch("port"); - const watchedSiteId = addTargetForm.watch("siteId"); - - const handleContainerSelect = (hostname: string, port?: number) => { - addTargetForm.setValue("ip", hostname); - if (port) { - addTargetForm.setValue("port", port); - } - }; - const tlsSettingsForm = useForm({ resolver: zodResolver(tlsSettingsSchema), defaultValues: { @@ -403,13 +388,7 @@ export default function ReverseProxyTargets(props: { initializeDockerForSite(site.siteId); } - // If there's only one site, set it as the default in the form - if (res.data.data.sites.length) { - addTargetForm.setValue( - "siteId", - res.data.data.sites[0].siteId - ); - } + // Sites loaded successfully } }; fetchSites(); @@ -438,6 +417,158 @@ export default function ReverseProxyTargets(props: { // fetchSite(); }, []); + // Save advanced mode preference to localStorage + useEffect(() => { + if (typeof window !== "undefined") { + localStorage.setItem( + "proxy-advanced-mode", + isAdvancedMode.toString() + ); + } + }, [isAdvancedMode]); + + function addNewTarget() { + const newTarget: LocalTarget = { + targetId: -Date.now(), // Use negative timestamp as temporary ID + ip: "", + method: resource.http ? "http" : null, + port: 0, + siteId: sites.length > 0 ? sites[0].siteId : 0, + path: null, + pathMatchType: null, + rewritePath: null, + rewritePathType: null, + priority: 100, + enabled: true, + resourceId: resource.resourceId, + hcEnabled: false, + hcPath: null, + hcMethod: null, + hcInterval: null, + hcTimeout: null, + hcHeaders: null, + hcScheme: null, + hcHostname: null, + hcPort: null, + hcFollowRedirects: null, + hcHealth: "unknown", + hcStatus: null, + hcMode: null, + hcUnhealthyInterval: null, + siteType: sites.length > 0 ? sites[0].type : null, + new: true, + updated: false + }; + + setTargets((prev) => [...prev, newTarget]); + } + + async function saveNewTarget(target: LocalTarget) { + // Validate the target + if (!isTargetValid(target.ip)) { + toast({ + variant: "destructive", + title: t("targetErrorInvalidIp"), + description: t("targetErrorInvalidIpDescription") + }); + return; + } + + if (!target.port || target.port <= 0) { + toast({ + variant: "destructive", + title: t("targetErrorInvalidPort"), + description: t("targetErrorInvalidPortDescription") + }); + return; + } + + if (!target.siteId) { + toast({ + variant: "destructive", + title: t("targetErrorNoSite"), + description: t("targetErrorNoSiteDescription") + }); + return; + } + + // Check if target with same IP, port and method already exists + const isDuplicate = targets.some( + (t) => + t.targetId !== target.targetId && + t.ip === target.ip && + t.port === target.port && + t.method === target.method && + t.siteId === target.siteId + ); + + if (isDuplicate) { + toast({ + variant: "destructive", + title: t("targetErrorDuplicate"), + description: t("targetErrorDuplicateDescription") + }); + return; + } + + try { + setTargetsLoading(true); + + const response = await api.post< + AxiosResponse + >(`/target`, { + resourceId: resource.resourceId, + siteId: target.siteId, + ip: target.ip, + method: target.method, + port: target.port, + path: target.path, + pathMatchType: target.pathMatchType, + rewritePath: target.rewritePath, + rewritePathType: target.rewritePathType, + priority: target.priority, + enabled: target.enabled, + hcEnabled: target.hcEnabled, + hcPath: target.hcPath, + hcInterval: target.hcInterval, + hcTimeout: target.hcTimeout + }); + + if (response.status === 200) { + // Update the target with the new ID and remove the new flag + setTargets((prev) => + prev.map((t) => + t.targetId === target.targetId + ? { + ...t, + targetId: response.data.data.targetId, + new: false, + updated: false + } + : t + ) + ); + + toast({ + title: t("targetCreated"), + description: t("targetCreatedDescription") + }); + } + } catch (err) { + console.error(err); + toast({ + variant: "destructive", + title: t("targetErrorCreate"), + description: formatAxiosError( + err, + t("targetErrorCreateDescription") + ) + }); + } finally { + setTargetsLoading(false); + } + } + async function addTarget(data: z.infer) { // Check if target with same IP, port and method already exists const isDuplicate = targets.some( @@ -514,16 +645,6 @@ export default function ReverseProxyTargets(props: { }; setTargets([...targets, newTarget]); - addTargetForm.reset({ - ip: "", - method: resource.http ? "http" : null, - port: "" as any as number, - path: null, - pathMatchType: null, - rewritePath: null, - rewritePathType: null, - priority: 100 - }); } const removeTarget = (targetId: number) => { @@ -573,6 +694,24 @@ export default function ReverseProxyTargets(props: { }; async function saveAllSettings() { + // Validate that no targets have blank IPs or invalid ports + const targetsWithInvalidFields = targets.filter( + (target) => + !target.ip || + target.ip.trim() === "" || + !target.port || + target.port <= 0 || + isNaN(target.port) + ); + if (targetsWithInvalidFields.length > 0) { + toast({ + variant: "destructive", + title: t("targetErrorInvalidIp"), + description: t("targetErrorInvalidIpDescription") + }); + return; + } + try { setTargetsLoading(true); setHttpsTlsLoading(true); @@ -673,23 +812,10 @@ export default function ReverseProxyTargets(props: { } } - const columns: ColumnDef[] = [ - { - accessorKey: "enabled", - header: t("enabled"), - cell: ({ row }) => ( - - updateTarget(row.original.targetId, { - ...row.original, - enabled: val - }) - } - /> - ) - }, - { + const getColumns = (): ColumnDef[] => { + const baseColumns: ColumnDef[] = []; + + const priorityColumn: ColumnDef = { id: "priority", header: () => (
@@ -713,13 +839,13 @@ export default function ReverseProxyTargets(props: { ), cell: ({ row }) => { return ( -
+
{ const value = parseInt(e.target.value, 10); if (value >= 1 && value <= 1000) { @@ -732,9 +858,13 @@ export default function ReverseProxyTargets(props: { />
); - } - }, - { + }, + size: 120, + minSize: 100, + maxSize: 150 + }; + + const healthCheckColumn: ColumnDef = { accessorKey: "healthCheck", header: t("healthCheck"), cell: ({ row }) => { @@ -778,43 +908,35 @@ export default function ReverseProxyTargets(props: { }; return ( - <> +
{row.original.siteType === "newt" ? ( ) : ( - + - )} - +
); - } - }, - { + }, + size: 200, + minSize: 180, + maxSize: 250 + }; + + const matchPathColumn: ColumnDef = { accessorKey: "path", header: t("matchPath"), cell: ({ row }) => { @@ -822,56 +944,61 @@ export default function ReverseProxyTargets(props: { row.original.path || row.original.pathMatchType ); - return hasPathMatch ? ( -
- - updateTarget(row.original.targetId, config) - } - trigger={ - - } - /> - -
- ) : ( -
- - updateTarget(row.original.targetId, config) - } - trigger={ - - } - /> - + return ( +
+ {hasPathMatch ? ( + + updateTarget(row.original.targetId, config) + } + trigger={ + + } + /> + ) : ( + + updateTarget(row.original.targetId, config) + } + trigger={ + + } + /> + )}
); - } - }, - { + }, + size: 200, + minSize: 180, + maxSize: 200 + }; + + const addressColumn: ColumnDef = { accessorKey: "address", header: t("address"), cell: ({ row }) => { @@ -896,25 +1023,24 @@ export default function ReverseProxyTargets(props: { }; return ( -
- @@ -1004,14 +1130,14 @@ export default function ReverseProxyTargets(props: { -
+
{"://"}
{ const input = e.target.value.trim(); const hasProtocol = @@ -1051,27 +1177,42 @@ export default function ReverseProxyTargets(props: { } }} /> -
+
{":"}
- updateTarget(row.original.targetId, { - ...row.original, - port: parseInt(e.target.value, 10) - }) + defaultValue={ + row.original.port === 0 + ? "" + : row.original.port } + className="w-[75px] pl-0 border-none placeholder-gray-400" + onBlur={(e) => { + const value = parseInt(e.target.value, 10); + if (!isNaN(value) && value > 0) { + updateTarget(row.original.targetId, { + ...row.original, + port: value + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + port: 0 + }); + } + }} /> - - +
); - } - }, - { + }, + size: 400, + minSize: 350, + maxSize: 500 + }; + + const rewritePathColumn: ColumnDef = { accessorKey: "rewritePath", header: t("rewritePath"), cell: ({ row }) => { @@ -1081,98 +1222,125 @@ export default function ReverseProxyTargets(props: { const noPathMatch = !row.original.path && !row.original.pathMatchType; - return hasRewritePath && !noPathMatch ? ( -
- - updateTarget(row.original.targetId, config) - } - trigger={ - - } - /> + return ( +
+ {hasRewritePath && !noPathMatch ? ( + + updateTarget(row.original.targetId, config) + } + trigger={ + + } + /> + ) : ( + + updateTarget(row.original.targetId, config) + } + trigger={ + + } + disabled={noPathMatch} + /> + )}
- ) : ( - - updateTarget(row.original.targetId, config) - } - trigger={ - - } - disabled={noPathMatch} - /> ); - } - }, + }, + size: 200, + minSize: 180, + maxSize: 200 + }; - // { - // accessorKey: "protocol", - // header: t('targetProtocol'), - // cell: ({ row }) => ( - // - // ), - // }, + const enabledColumn: ColumnDef = { + accessorKey: "enabled", + header: t("enabled"), + cell: ({ row }) => ( +
+ + updateTarget(row.original.targetId, { + ...row.original, + enabled: val + }) + } + /> +
+ ), + size: 100, + minSize: 80, + maxSize: 120 + }; - { + const actionsColumn: ColumnDef = { id: "actions", cell: ({ row }) => ( - <> -
- {/* */} +
+ +
+ ), + size: 100, + minSize: 80, + maxSize: 120 + }; - -
- - ) + if (isAdvancedMode) { + return [ + matchPathColumn, + addressColumn, + rewritePathColumn, + priorityColumn, + healthCheckColumn, + enabledColumn, + actionsColumn + ]; + } else { + return [ + addressColumn, + healthCheckColumn, + enabledColumn, + actionsColumn + ]; } - ]; + }; + + const columns = getColumns(); const table = useReactTable({ data: targets, @@ -1203,351 +1371,8 @@ export default function ReverseProxyTargets(props: { -
-
- -
- ( - - - {t("site")} - -
- - - - - - - - - - - - {t( - "siteNotFound" - )} - - - {sites.map( - ( - site - ) => ( - { - addTargetForm.setValue( - "siteId", - site.siteId - ); - }} - > - - { - site.name - } - - ) - )} - - - - - - - {field.value && - (() => { - const selectedSite = - sites.find( - (site) => - site.siteId === - field.value - ); - return selectedSite && - selectedSite.type === - "newt" - ? (() => { - const dockerState = - getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })() - : null; - })()} -
- -
- )} - /> - - {resource.http && ( - ( - - - {t("method")} - - - - - - - )} - /> - )} - - ( - - - {t("targetAddr")} - - - { - const input = - e.target.value.trim(); - const hasProtocol = - /^(https?|h2c):\/\//.test( - input - ); - const hasPort = - /:\d+(?:\/|$)/.test( - input - ); - - if ( - hasProtocol || - hasPort - ) { - const parsed = - parseHostTarget( - input - ); - if (parsed) { - if ( - hasProtocol || - !addTargetForm.getValues( - "method" - ) - ) { - addTargetForm.setValue( - "method", - parsed.protocol - ); - } - addTargetForm.setValue( - "ip", - parsed.host - ); - if ( - hasPort || - !addTargetForm.getValues( - "port" - ) - ) { - addTargetForm.setValue( - "port", - parsed.port - ); - } - } - } else { - field.onBlur(); - } - }} - /> - - - - )} - /> - ( - - - {t("targetPort")} - - - - - - - )} - /> - -
- - -
- {targets.length > 0 ? ( <> -
- {t("targetsList")} -
- -
- - ( - - - { - field.onChange( - val - ); - }} - /> - - - )} - /> - - -
@@ -1616,12 +1441,40 @@ export default function ReverseProxyTargets(props: { {/* */}
+
+
+ +
+ + +
+
+
) : ( -
-

+

+

{t("targetNoOne")}

+
)} @@ -1659,6 +1512,9 @@ export default function ReverseProxyTargets(props: { label={t( "proxyEnableSSL" )} + description={t( + "proxyEnableSSLDescription" + )} defaultChecked={ field.value } @@ -1699,6 +1555,46 @@ export default function ReverseProxyTargets(props: { + +
+ + ( + + + { + field.onChange(val); + }} + /> + + + )} + /> + + +
+
( - + {t("customHeaders")} diff --git a/src/components/HealthCheckDialog.tsx b/src/components/HealthCheckDialog.tsx index 1942e1d8..6fa36a5b 100644 --- a/src/components/HealthCheckDialog.tsx +++ b/src/components/HealthCheckDialog.tsx @@ -107,7 +107,7 @@ export default function HealthCheckDialog({ useEffect(() => { if (!open) return; - + // Determine default scheme from target method const getDefaultScheme = () => { if (initialConfig?.hcScheme) { @@ -177,7 +177,7 @@ export default function HealthCheckDialog({ render={({ field }) => (
- + {t("enableHealthChecks")} @@ -210,7 +210,7 @@ export default function HealthCheckDialog({ name="hcScheme" render={({ field }) => ( - + {t("healthScheme")} ( - + {t( "healthyIntervalSeconds" )} @@ -425,7 +425,7 @@ export default function HealthCheckDialog({ name="hcUnhealthyInterval" render={({ field }) => ( - + {t( "unhealthyIntervalSeconds" )} @@ -460,7 +460,7 @@ export default function HealthCheckDialog({ name="hcTimeout" render={({ field }) => ( - + {t("timeoutSeconds")} @@ -499,7 +499,7 @@ export default function HealthCheckDialog({ name="hcStatus" render={({ field }) => ( - + {t("expectedResponseCodes")} @@ -541,7 +541,7 @@ export default function HealthCheckDialog({ name="hcHeaders" render={({ field }) => ( - + {t("customHeaders")} diff --git a/src/components/PathMatchRenameModal.tsx b/src/components/PathMatchRenameModal.tsx index f67f44c3..13d83bd1 100644 --- a/src/components/PathMatchRenameModal.tsx +++ b/src/components/PathMatchRenameModal.tsx @@ -1,4 +1,4 @@ -import { Pencil } from "lucide-react"; +import { Settings } from "lucide-react"; import { Select, SelectContent, @@ -256,7 +256,7 @@ export function PathMatchDisplay({ {value.path} - +
); } @@ -287,7 +287,7 @@ export function PathRewriteDisplay({ {value.rewritePath || (strip)} - +
); } diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx index dcfc646d..83e84073 100644 --- a/src/components/ui/textarea.tsx +++ b/src/components/ui/textarea.tsx @@ -10,7 +10,7 @@ const Textarea = React.forwardRef( return (