Merge remote-tracking branch 'origin' into groups-phase-2b

This commit is contained in:
Tuan Dang
2024-04-22 10:37:53 -07:00
53 changed files with 1112 additions and 450 deletions

View File

@ -5,6 +5,7 @@ on:
types: [opened, synchronize]
paths:
- "backend/src/server/routes/**"
- "backend/src/ee/routes/**"
jobs:
check-be-api-changes:

View File

@ -47,10 +47,11 @@
"libsodium-wrappers": "^0.7.13",
"lodash.isequal": "^4.5.0",
"ms": "^2.1.3",
"mysql2": "^3.9.1",
"mysql2": "^3.9.4",
"nanoid": "^5.0.4",
"nodemailer": "^6.9.9",
"ora": "^7.0.1",
"oracledb": "^6.4.0",
"passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0",
"passport-google-oauth20": "^2.0.0",
@ -1708,6 +1709,22 @@
"node": ">=12"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@ -3162,9 +3179,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.8.0.tgz",
"integrity": "sha512-zdTObFRoNENrdPpnTNnhOljYIcOX7aI7+7wyrSpPFFIOf/nRdedE6IYsjaBE7tjukphh1tMTojgJ7p3lKY8x6Q==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz",
"integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==",
"cpu": [
"arm"
],
@ -3175,9 +3192,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.8.0.tgz",
"integrity": "sha512-aiItwP48BiGpMFS9Znjo/xCNQVwTQVcRKkFKsO81m8exrGjHkCBDvm9PHay2kpa8RPnZzzKcD1iQ9KaLY4fPQQ==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz",
"integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==",
"cpu": [
"arm64"
],
@ -3188,9 +3205,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.8.0.tgz",
"integrity": "sha512-zhNIS+L4ZYkYQUjIQUR6Zl0RXhbbA0huvNIWjmPc2SL0cB1h5Djkcy+RZ3/Bwszfb6vgwUvcVJYD6e6Zkpsi8g==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz",
"integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==",
"cpu": [
"arm64"
],
@ -3201,9 +3218,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.8.0.tgz",
"integrity": "sha512-A/FAHFRNQYrELrb/JHncRWzTTXB2ticiRFztP4ggIUAfa9Up1qfW8aG2w/mN9jNiZ+HB0t0u0jpJgFXG6BfRTA==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz",
"integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==",
"cpu": [
"x64"
],
@ -3214,9 +3231,22 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.8.0.tgz",
"integrity": "sha512-JsidBnh3p2IJJA4/2xOF2puAYqbaczB3elZDT0qHxn362EIoIkq7hrR43Xa8RisgI6/WPfvb2umbGsuvf7E37A==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz",
"integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz",
"integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==",
"cpu": [
"arm"
],
@ -3227,9 +3257,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.8.0.tgz",
"integrity": "sha512-hBNCnqw3EVCkaPB0Oqd24bv8SklETptQWcJz06kb9OtiShn9jK1VuTgi7o4zPSt6rNGWQOTDEAccbk0OqJmS+g==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz",
"integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==",
"cpu": [
"arm64"
],
@ -3240,9 +3270,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.8.0.tgz",
"integrity": "sha512-Fw9ChYfJPdltvi9ALJ9wzdCdxGw4wtq4t1qY028b2O7GwB5qLNSGtqMsAel1lfWTZvf4b6/+4HKp0GlSYg0ahA==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz",
"integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==",
"cpu": [
"arm64"
],
@ -3252,10 +3282,23 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz",
"integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.8.0.tgz",
"integrity": "sha512-BH5xIh7tOzS9yBi8dFrCTG8Z6iNIGWGltd3IpTSKp6+pNWWO6qy8eKoRxOtwFbMrid5NZaidLYN6rHh9aB8bEw==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz",
"integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==",
"cpu": [
"riscv64"
],
@ -3265,10 +3308,23 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz",
"integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.8.0.tgz",
"integrity": "sha512-PmvAj8k6EuWiyLbkNpd6BLv5XeYFpqWuRvRNRl80xVfpGXK/z6KYXmAgbI4ogz7uFiJxCnYcqyvZVD0dgFog7Q==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz",
"integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==",
"cpu": [
"x64"
],
@ -3279,9 +3335,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.8.0.tgz",
"integrity": "sha512-mdxnlW2QUzXwY+95TuxZ+CurrhgrPAMveDWI97EQlA9bfhR8tw3Pt7SUlc/eSlCNxlWktpmT//EAA8UfCHOyXg==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz",
"integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==",
"cpu": [
"x64"
],
@ -3292,9 +3348,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.8.0.tgz",
"integrity": "sha512-ge7saUz38aesM4MA7Cad8CHo0Fyd1+qTaqoIo+Jtk+ipBi4ATSrHWov9/S4u5pbEQmLjgUjB7BJt+MiKG2kzmA==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz",
"integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==",
"cpu": [
"arm64"
],
@ -3305,9 +3361,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.8.0.tgz",
"integrity": "sha512-p9E3PZlzurhlsN5h9g7zIP1DnqKXJe8ZUkFwAazqSvHuWfihlIISPxG9hCHCoA+dOOspL/c7ty1eeEVFTE0UTw==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz",
"integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==",
"cpu": [
"ia32"
],
@ -3318,9 +3374,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.8.0.tgz",
"integrity": "sha512-kb4/auKXkYKqlUYTE8s40FcJIj5soOyRLHKd4ugR0dCq0G2EfcF54eYcfQiGkHzjidZ40daB4ulsFdtqNKZtBg==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz",
"integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==",
"cpu": [
"x64"
],
@ -5916,12 +5972,12 @@
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -5929,7 +5985,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@ -7379,16 +7435,16 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@ -7419,6 +7475,14 @@
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@ -7749,9 +7813,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
@ -9759,9 +9823,9 @@
}
},
"node_modules/mysql2": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz",
"integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==",
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.4.tgz",
"integrity": "sha512-OEESQuwxMza803knC1YSt7NMuc1BrK9j7gZhCSs2WAyxr1vfiI7QLaLOKTh5c9SWGz98qVyQUbK8/WckevNQhg==",
"dependencies": {
"denque": "^2.1.0",
"generate-function": "^2.3.1",
@ -10231,6 +10295,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/oracledb": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/oracledb/-/oracledb-6.4.0.tgz",
"integrity": "sha512-TJI08qzQlf/l7T49VojP9BoQpjEr14NXZmpSzzcLrbNs7qSl0QA/Mc9gGiEdkg5WmwH0wqUjtMC7jlf1WamlYA==",
"hasInstallScript": true,
"engines": {
"node": ">=14.6"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -10818,9 +10891,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"funding": [
{
@ -10839,7 +10912,7 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@ -11234,9 +11307,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -11511,10 +11584,13 @@
}
},
"node_modules/rollup": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.8.0.tgz",
"integrity": "sha512-NpsklK2fach5CdI+PScmlE5R4Ao/FSWtF7LkoIrHDxPACY/xshNasPsbpG0VVHxUTbf74tJbVT4PrP8JsJ6ZDA==",
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz",
"integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.5"
},
"bin": {
"rollup": "dist/bin/rollup"
},
@ -11523,19 +11599,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.8.0",
"@rollup/rollup-android-arm64": "4.8.0",
"@rollup/rollup-darwin-arm64": "4.8.0",
"@rollup/rollup-darwin-x64": "4.8.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.8.0",
"@rollup/rollup-linux-arm64-gnu": "4.8.0",
"@rollup/rollup-linux-arm64-musl": "4.8.0",
"@rollup/rollup-linux-riscv64-gnu": "4.8.0",
"@rollup/rollup-linux-x64-gnu": "4.8.0",
"@rollup/rollup-linux-x64-musl": "4.8.0",
"@rollup/rollup-win32-arm64-msvc": "4.8.0",
"@rollup/rollup-win32-ia32-msvc": "4.8.0",
"@rollup/rollup-win32-x64-msvc": "4.8.0",
"@rollup/rollup-android-arm-eabi": "4.14.3",
"@rollup/rollup-android-arm64": "4.14.3",
"@rollup/rollup-darwin-arm64": "4.14.3",
"@rollup/rollup-darwin-x64": "4.14.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.14.3",
"@rollup/rollup-linux-arm-musleabihf": "4.14.3",
"@rollup/rollup-linux-arm64-gnu": "4.14.3",
"@rollup/rollup-linux-arm64-musl": "4.14.3",
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.3",
"@rollup/rollup-linux-riscv64-gnu": "4.14.3",
"@rollup/rollup-linux-s390x-gnu": "4.14.3",
"@rollup/rollup-linux-x64-gnu": "4.14.3",
"@rollup/rollup-linux-x64-musl": "4.14.3",
"@rollup/rollup-win32-arm64-msvc": "4.14.3",
"@rollup/rollup-win32-ia32-msvc": "4.14.3",
"@rollup/rollup-win32-x64-msvc": "4.14.3",
"fsevents": "~2.3.2"
}
},
@ -11887,9 +11966,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@ -12249,9 +12328,9 @@
}
},
"node_modules/tar": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@ -13447,14 +13526,14 @@
}
},
"node_modules/vite": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
"integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
"version": "5.2.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz",
"integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==",
"dev": true,
"dependencies": {
"esbuild": "^0.19.3",
"postcss": "^8.4.32",
"rollup": "^4.2.0"
"esbuild": "^0.20.1",
"postcss": "^8.4.38",
"rollup": "^4.13.0"
},
"bin": {
"vite": "bin/vite.js"
@ -13589,9 +13668,9 @@
"dev": true
},
"node_modules/vite/node_modules/@esbuild/android-arm": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz",
"integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"cpu": [
"arm"
],
@ -13605,9 +13684,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/android-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz",
"integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"cpu": [
"arm64"
],
@ -13621,9 +13700,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/android-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz",
"integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"cpu": [
"x64"
],
@ -13637,9 +13716,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/darwin-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz",
"integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"cpu": [
"arm64"
],
@ -13653,9 +13732,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/darwin-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz",
"integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"cpu": [
"x64"
],
@ -13669,9 +13748,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz",
"integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"cpu": [
"arm64"
],
@ -13685,9 +13764,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/freebsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz",
"integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"cpu": [
"x64"
],
@ -13701,9 +13780,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-arm": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz",
"integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"cpu": [
"arm"
],
@ -13717,9 +13796,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz",
"integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"cpu": [
"arm64"
],
@ -13733,9 +13812,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-ia32": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz",
"integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"cpu": [
"ia32"
],
@ -13749,9 +13828,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-loong64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz",
"integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"cpu": [
"loong64"
],
@ -13765,9 +13844,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-mips64el": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz",
"integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"cpu": [
"mips64el"
],
@ -13781,9 +13860,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-ppc64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz",
"integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"cpu": [
"ppc64"
],
@ -13797,9 +13876,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-riscv64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz",
"integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"cpu": [
"riscv64"
],
@ -13813,9 +13892,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-s390x": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz",
"integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"cpu": [
"s390x"
],
@ -13829,9 +13908,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/linux-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz",
"integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"cpu": [
"x64"
],
@ -13845,9 +13924,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/netbsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz",
"integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"cpu": [
"x64"
],
@ -13861,9 +13940,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/openbsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz",
"integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"cpu": [
"x64"
],
@ -13877,9 +13956,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/sunos-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz",
"integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"cpu": [
"x64"
],
@ -13893,9 +13972,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/win32-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz",
"integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"cpu": [
"arm64"
],
@ -13909,9 +13988,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/win32-ia32": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz",
"integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"cpu": [
"ia32"
],
@ -13925,9 +14004,9 @@
}
},
"node_modules/vite/node_modules/@esbuild/win32-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz",
"integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"cpu": [
"x64"
],
@ -13941,9 +14020,9 @@
}
},
"node_modules/vite/node_modules/esbuild": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz",
"integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -13953,28 +14032,29 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.19.9",
"@esbuild/android-arm64": "0.19.9",
"@esbuild/android-x64": "0.19.9",
"@esbuild/darwin-arm64": "0.19.9",
"@esbuild/darwin-x64": "0.19.9",
"@esbuild/freebsd-arm64": "0.19.9",
"@esbuild/freebsd-x64": "0.19.9",
"@esbuild/linux-arm": "0.19.9",
"@esbuild/linux-arm64": "0.19.9",
"@esbuild/linux-ia32": "0.19.9",
"@esbuild/linux-loong64": "0.19.9",
"@esbuild/linux-mips64el": "0.19.9",
"@esbuild/linux-ppc64": "0.19.9",
"@esbuild/linux-riscv64": "0.19.9",
"@esbuild/linux-s390x": "0.19.9",
"@esbuild/linux-x64": "0.19.9",
"@esbuild/netbsd-x64": "0.19.9",
"@esbuild/openbsd-x64": "0.19.9",
"@esbuild/sunos-x64": "0.19.9",
"@esbuild/win32-arm64": "0.19.9",
"@esbuild/win32-ia32": "0.19.9",
"@esbuild/win32-x64": "0.19.9"
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
}
},
"node_modules/vitest": {

View File

@ -112,6 +112,7 @@
"nanoid": "^5.0.4",
"nodemailer": "^6.9.9",
"ora": "^7.0.1",
"oracledb": "^6.4.0",
"passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0",
"passport-google-oauth20": "^2.0.0",

View File

@ -19,7 +19,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
description: "Return project secret snapshots ids",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
@ -97,8 +96,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
description: "Return audit logs",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -69,7 +69,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
description: "Roll back project secrets to those captured in a secret snapshot version.",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],

View File

@ -2,7 +2,8 @@ import { z } from "zod";
export enum SqlProviders {
Postgres = "postgres",
MySQL = "mysql2"
MySQL = "mysql2",
Oracle = "oracledb"
}
export const DynamicSecretSqlDBSchema = z.object({

View File

@ -8,15 +8,25 @@ import { BadRequestError } from "@app/lib/errors";
import { getDbConnectionHost } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretSqlDBSchema, TDynamicProviderFns } from "./models";
import { DynamicSecretSqlDBSchema, SqlProviders, TDynamicProviderFns } from "./models";
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
const generatePassword = (size?: number) => {
const generatePassword = (provider: SqlProviders) => {
// oracle has limit of 48 password length
const size = provider === SqlProviders.Oracle ? 30 : 48;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
return customAlphabet(charset, 48)(size);
};
const generateUsername = (provider: SqlProviders) => {
// For oracle, the client assumes everything is upper case when not using quotes around the password
if (provider === SqlProviders.Oracle) return alphaNumericNanoId(32).toUpperCase();
return alphaNumericNanoId(32);
};
export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const validateProviderInputs = async (inputs: unknown) => {
const appCfg = getConfig();
@ -59,10 +69,10 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const isConnected = await db
.raw("SELECT NOW()")
.then(() => true)
.catch(() => false);
// oracle needs from keyword
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
const isConnected = await db.raw(testStatement).then(() => true);
await db.destroy();
return isConnected;
};
@ -71,8 +81,8 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const username = alphaNumericNanoId(32);
const password = generatePassword();
const username = generateUsername(providerInputs.client);
const password = generatePassword(providerInputs.client);
const { database } = providerInputs;
const expiration = new Date(expireAt).toISOString();

View File

@ -1,4 +1,4 @@
import { ForbiddenError } from "@casl/ability";
import { ForbiddenError, subject } from "@casl/ability";
import { TableName, TSecretTagJunctionInsert } from "@app/db/schemas";
import { BadRequestError, InternalServerError } from "@app/lib/errors";
@ -23,6 +23,7 @@ import {
import { TSnapshotDALFactory } from "./snapshot-dal";
import { TSnapshotFolderDALFactory } from "./snapshot-folder-dal";
import { TSnapshotSecretDALFactory } from "./snapshot-secret-dal";
import { getFullFolderPath } from "./snapshot-service-fns";
type TSecretSnapshotServiceFactoryDep = {
snapshotDAL: TSnapshotDALFactory;
@ -33,7 +34,7 @@ type TSecretSnapshotServiceFactoryDep = {
secretDAL: Pick<TSecretDALFactory, "delete" | "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
folderDAL: Pick<TSecretFolderDALFactory, "findById" | "findBySecretPath" | "delete" | "insertMany">;
folderDAL: Pick<TSecretFolderDALFactory, "findById" | "findBySecretPath" | "delete" | "insertMany" | "find">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "isValidLicense">;
};
@ -71,6 +72,12 @@ export const secretSnapshotServiceFactory = ({
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
@ -98,6 +105,12 @@ export const secretSnapshotServiceFactory = ({
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
@ -116,6 +129,19 @@ export const secretSnapshotServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const fullFolderPath = await getFullFolderPath({
folderDAL,
folderId: snapshot.folderId,
envId: snapshot.environment.id
});
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: snapshot.environment.slug, secretPath: fullFolderPath })
);
return snapshot;
};

View File

@ -101,6 +101,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
key: "snapshotId",
parentMapper: ({
snapshotId: id,
folderId,
projectId,
envId,
envSlug,
@ -109,6 +110,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
snapshotUpdatedAt: updatedAt
}) => ({
id,
folderId,
projectId,
createdAt,
updatedAt,

View File

@ -0,0 +1,28 @@
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
type GetFullFolderPath = {
folderDAL: Pick<TSecretFolderDALFactory, "findById" | "find">; // Added findAllInEnv
folderId: string;
envId: string;
};
export const getFullFolderPath = async ({ folderDAL, folderId, envId }: GetFullFolderPath): Promise<string> => {
// Helper function to remove duplicate slashes
const removeDuplicateSlashes = (path: string) => path.replace(/\/{2,}/g, "/");
// Fetch all folders at once based on environment ID to avoid multiple queries
const folders = await folderDAL.find({ envId });
const folderMap = new Map(folders.map((folder) => [folder.id, folder]));
const buildPath = (currFolderId: string): string => {
const folder = folderMap.get(currFolderId);
if (!folder) return "";
const folderPathSegment = !folder.parentId && folder.name === "root" ? "/" : `/${folder.name}`;
if (folder.parentId) {
return removeDuplicateSlashes(`${buildPath(folder.parentId)}${folderPathSegment}`);
}
return removeDuplicateSlashes(folderPathSegment);
};
return buildPath(folderId);
};

View File

@ -589,7 +589,8 @@ export const INTEGRATION = {
secretSuffix: "The suffix for the saved secret. Used by GCP",
initialSyncBehavoir: "Type of syncing behavoir with the integration",
shouldAutoRedeploy: "Used by Render to trigger auto deploy",
secretGCPLabel: "The label for the GCP secrets"
secretGCPLabel: "The label for the GCP secrets",
secretAWSTag: "The tag for the AWS secrets"
}
},
UPDATE: {

View File

@ -30,12 +30,6 @@ export const fastifySwagger = fp(async (fastify) => {
scheme: "bearer",
bearerFormat: "JWT",
description: "An access token in Infisical"
},
apiKeyAuth: {
type: "apiKey",
in: "header",
name: "X-API-Key",
description: "An API Key in Infisical"
}
}
}

View File

@ -56,7 +56,14 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
labelValue: z.string()
})
.optional()
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel)
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
secretAWSTag: z
.object({
key: z.string(),
value: z.string()
})
.optional()
.describe(INTEGRATION.CREATE.metadata.secretAWSTag)
})
.optional()
}),

View File

@ -18,8 +18,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Create environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -77,8 +76,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Update environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -144,8 +142,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Delete environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -26,8 +26,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Return project user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -139,8 +138,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Update project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -213,8 +211,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Delete project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -138,6 +138,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: readLimit
},
schema: {
description: "Get project",
security: [
{
bearerAuth: []
}
],
params: z.object({
workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId)
}),
@ -170,6 +176,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: writeLimit
},
schema: {
description: "Delete project",
security: [
{
bearerAuth: []
}
],
params: z.object({
workspaceId: z.string().trim().describe(PROJECTS.DELETE.workspaceId)
}),
@ -239,6 +251,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: writeLimit
},
schema: {
description: "Update project",
security: [
{
bearerAuth: []
}
],
params: z.object({
workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId)
}),

View File

@ -19,8 +19,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Create folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
body: z.object({
@ -76,8 +75,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Update folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -140,8 +138,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Delete a folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -200,8 +197,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Get folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
querystring: z.object({

View File

@ -19,8 +19,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Create secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
body: z.object({
@ -84,8 +83,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Update secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -159,8 +157,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Delete secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -223,8 +220,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Get secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
querystring: z.object({

View File

@ -18,8 +18,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
description: "Return organization identity memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -17,8 +17,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Return organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -65,8 +64,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Return projects in organization that user is part of",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -114,8 +112,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Update organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -157,8 +154,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Delete organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -15,6 +15,12 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
rateLimit: writeLimit
},
schema: {
description: "Invite members to project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().describe(PROJECTS.INVITE_MEMBER.projectId)
}),
@ -64,10 +70,15 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
rateLimit: writeLimit
},
schema: {
description: "Remove members from project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().describe(PROJECTS.REMOVE_MEMBER.projectId)
}),
body: z.object({
emails: z.string().email().array().default([]).describe(PROJECTS.REMOVE_MEMBER.emails),
usernames: z.string().array().default([]).describe(PROJECTS.REMOVE_MEMBER.usernames)

View File

@ -36,11 +36,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Return encrypted project key",
security: [
{
apiKeyAuth: []
}
],
params: z.object({
workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId)
}),
@ -149,6 +144,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: creationLimit
},
schema: {
description: "Create a new project",
security: [
{
bearerAuth: []
}
],
body: z.object({
projectName: z.string().trim().describe(PROJECTS.CREATE.projectName),
slug: z
@ -200,6 +201,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: writeLimit
},
schema: {
description: "Delete project",
security: [
{
bearerAuth: []
}
],
params: z.object({
slug: slugSchema.describe("The slug of the project to delete.")
}),

View File

@ -85,11 +85,6 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Return organizations that current user is part of",
security: [
{
apiKeyAuth: []
}
],
response: {
200: z.object({
organizations: OrganizationsSchema.array()
@ -217,11 +212,6 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Retrieve the current user on the request",
security: [
{
apiKeyAuth: []
}
],
response: {
200: z.object({
user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))

View File

@ -158,8 +158,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "List secrets",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
querystring: z.object({
@ -280,8 +279,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Get a secret by name",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -375,8 +373,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Create secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -464,8 +461,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Update secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({
@ -550,8 +546,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Delete secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
bearerAuth: []
}
],
params: z.object({

View File

@ -129,26 +129,55 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
* Return list of names of apps for Vercel integration
*/
const getAppsVercel = async ({ accessToken, teamId }: { teamId?: string | null; accessToken: string }) => {
const res = (
await request.get<{ projects: { name: string; id: string }[] }>(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, {
const apps: Array<{ name: string; appId: string }> = [];
const limit = "20";
let hasMorePages = true;
let next: number | null = null;
interface Response {
projects: { name: string; id: string }[];
pagination: {
count: number;
next: number | null;
prev: number;
};
}
while (hasMorePages) {
const params: { [key: string]: string } = {
limit
};
if (teamId) {
params.teamId = teamId;
}
if (next) {
params.until = String(next);
}
const { data } = await request.get<Response>(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, {
params: new URLSearchParams(params),
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
},
...(teamId
? {
params: {
teamId
}
}
: {})
})
).data;
}
});
const apps = res.projects.map((a) => ({
name: a.name,
appId: a.id
}));
data.projects.forEach((a) => {
apps.push({
name: a.name,
appId: a.id
});
});
next = data.pagination.next;
if (data.pagination.next === null) {
hasMorePages = false;
}
}
return apps;
};

View File

@ -457,6 +457,8 @@ const syncSecretsAWSParameterStore = async ({
});
ssm.config.update(config);
const metadata = z.record(z.any()).parse(integration.metadata);
const params = {
Path: integration.path as string,
Recursive: false,
@ -486,7 +488,8 @@ const syncSecretsAWSParameterStore = async ({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
Overwrite: true
// Overwrite: true,
Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
.promise();
// case: secret exists in AWS parameter store
@ -499,6 +502,7 @@ const syncSecretsAWSParameterStore = async ({
Type: "SecureString",
Value: secrets[key].value,
Overwrite: true
// Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
.promise();
}
@ -537,6 +541,7 @@ const syncSecretsAWSSecretManager = async ({
}) => {
let secretsManager;
const secKeyVal = getSecretKeyValuePair(secrets);
const metadata = z.record(z.any()).parse(integration.metadata);
try {
if (!accessId) return;
@ -573,7 +578,8 @@ const syncSecretsAWSSecretManager = async ({
await secretsManager.send(
new CreateSecretCommand({
Name: integration.app as string,
SecretString: JSON.stringify(secKeyVal)
SecretString: JSON.stringify(secKeyVal),
Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
);
}

View File

@ -22,6 +22,10 @@ export type TCreateIntegrationDTO = {
labelName: string;
labelValue: string;
};
secretAWSTag?: {
key: string;
value: string;
};
};
} & Omit<TProjectPermission, "projectId">;

View File

@ -134,8 +134,7 @@ export const projectMembershipServiceFactory = ({
const projectMemberships = await projectMembershipDAL.insertMany(
orgMembers.map(({ userId }) => ({
projectId,
userId: userId as string,
role: ProjectMembershipRole.Member
userId: userId as string
})),
tx
);
@ -267,8 +266,7 @@ export const projectMembershipServiceFactory = ({
const projectMemberships = await projectMembershipDAL.insertMany(
orgMembers.map(({ user }) => ({
projectId,
userId: user.id,
role: ProjectMembershipRole.Member
userId: user.id
})),
tx
);

View File

@ -381,6 +381,12 @@ func ProcessTemplate(templateId int, templatePath string, data interface{}, acce
funcs := template.FuncMap{
"secret": secretFunction,
"dynamic_secret": dynamicSecretFunction,
"minus": func(a, b int) int {
return a - b
},
"add": func(a, b int) int {
return a + b
},
}
templateName := path.Base(templatePath)

View File

@ -7,6 +7,7 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/Infisical/infisical-merge/packages/models"
@ -59,6 +60,11 @@ var exportCmd = &cobra.Command{
util.HandleError(err)
}
templatePath, err := cmd.Flags().GetString("template")
if err != nil {
util.HandleError(err)
}
secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
if err != nil {
util.HandleError(err, "Unable to parse flag")
@ -93,6 +99,31 @@ var exportCmd = &cobra.Command{
request.UniversalAuthAccessToken = token.Token
}
if templatePath != "" {
sigChan := make(chan os.Signal, 1)
dynamicSecretLeases := NewDynamicSecretLeaseManager(sigChan)
newEtag := ""
accessToken := ""
if token != nil {
accessToken = token.Token
} else {
log.Debug().Msg("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details")
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
util.HandleError(err)
}
accessToken = loggedInUserDetails.UserCredentials.JTWToken
}
processedTemplate, err := ProcessTemplate(1, templatePath, nil, accessToken, "", &newEtag, dynamicSecretLeases)
if err != nil {
util.HandleError(err)
}
fmt.Print(processedTemplate.String())
return
}
secrets, err := util.GetAllEnvironmentVariables(request, "")
if err != nil {
util.HandleError(err, "Unable to fetch secrets")
@ -140,6 +171,7 @@ func init() {
exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs")
exportCmd.Flags().String("projectId", "", "manually set the projectId to fetch secrets from")
exportCmd.Flags().String("path", "/", "get secrets within a folder path")
exportCmd.Flags().String("template", "", "The path to the template file used to render secrets")
}
// Format according to the format flag

View File

@ -33,6 +33,9 @@ Export environment variables from the platform into a file format.
# Export variables to a YAML file
infisical export --format=yaml > secrets.yaml
# Render secrets using a custom template file
infisical export --template=<path to template>
```
### Environment variables
@ -57,6 +60,26 @@ Export environment variables from the platform into a file format.
</Accordion>
### flags
<Accordion title="--template">
The `--template` flag specifies the path to the template file used for rendering secrets. When using templates, you can omit the other format flags.
```text my-template-file
{{$secrets := secret "<infisical-project-id>" "<environment-slug>" "<folder-path>"}}
{{$length := len $secrets}}
{{- "{"}}
{{- with $secrets }}
{{- range $index, $secret := . }}
"{{ $secret.Key }}": "{{ $secret.Value }}"{{if lt $index (minus $length 1)}},{{end}}
{{- end }}
{{- end }}
{{ "}" -}}
```
```bash
# Example
infisical export --template="/path/to/template/file"
```
</Accordion>
<Accordion title="--env">
Used to set the environment that secrets are pulled from.

View File

@ -0,0 +1,115 @@
---
title: "MySQL"
description: "Learn how to dynamically generate MySQL Database user passwords."
---
The Infisical MySQL dynamic secret allows you to generate MySQL Database credentials on demand based on configured role.
## Prerequisite
Create a user with the required permission in your SQL instance. This user will be used to create new accounts on-demand.
## Set up Dynamic Secrets with MySQL
<Steps>
<Step title="Open Secret Overview Dashboard">
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
</Step>
<Step title="Click on the 'Add Dynamic Secret' button">
![Add Dynamic Secret Button](../../../images/platform/dynamic-secrets/add-dynamic-secret-button.png)
</Step>
<Step title="Select 'SQL Database'">
![Dynamic Secret Modal](../../../images/platform/dynamic-secrets/dynamic-secret-modal.png)
</Step>
<Step title="Provide the inputs for dynamic secret parameters">
<ParamField path="Secret Name" type="string" required>
Name by which you want the secret to be referenced
</ParamField>
<ParamField path="Default TTL" type="string" required>
Default time-to-live for a generated secret (it is possible to modify this value when a secret is generate)
</ParamField>
<ParamField path="Max TTL" type="string" required>
Maximum time-to-live for a generated secret
</ParamField>
<ParamField path="Service" type="string" required>
Choose the service you want to generate dynamic secrets for. This must be selected as **MySQL**.
</ParamField>
<ParamField path="Host" type="string" required>
Database host
</ParamField>
<ParamField path="Port" type="number" required>
Database port
</ParamField>
<ParamField path="User" type="string" required>
Username that will be used to create dynamic secrets
</ParamField>
<ParamField path="Password" type="string" required>
Password that will be used to create dynamic secrets
</ParamField>
<ParamField path="Database Name" type="string" required>
Name of the database for which you want to create dynamic secrets
</ParamField>
<ParamField path="CA(SSL)" type="string">
A CA may be required if your DB requires it for incoming connections. AWS RDS instances with default settings will requires a CA which can be downloaded [here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions).
</ParamField>
</Step>
<Step title="(Optional) Modify SQL Statements">
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
![Modify SQL Statements Modal](/images/platform/dynamic-secrets/modify-sql-statement-mysql.png)
</Step>
<Step title="Click `Submit`">
After submitting the form, you will see a dynamic secret created in the dashboard.
<Note>
If this step fails, you may have to add the CA certificate.
</Note>
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret.png)
</Step>
<Step title="Generate dynamic secrets">
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-generate.png)
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-lease-empty.png)
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
![Provision Lease](/images/platform/dynamic-secrets/provision-lease.png)
<Tip>
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
</Tip>
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you.
![Provision Lease](/images/platform/dynamic-secrets/lease-values.png)
</Step>
</Steps>
## Audit or Revoke Leases
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
This will allow you see the expiration time of the lease or delete a lease before it's set time to live.
![Provision Lease](/images/platform/dynamic-secrets/lease-data.png)
## Renew Leases
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** as illustrated below.
![Provision Lease](/images/platform/dynamic-secrets/dynamic-secret-lease-renew.png)
<Warning>
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
</Warning>

View File

@ -0,0 +1,115 @@
---
title: "Oracle"
description: "Learn how to dynamically generate Oracle Database user passwords."
---
The Infisical Oracle dynamic secret allows you to generate Oracle Database credentials on demand based on configured role.
## Prerequisite
Create a user with the required permission in your SQL instance. This user will be used to create new accounts on-demand.
## Set up Dynamic Secrets with Oracle
<Steps>
<Step title="Open Secret Overview Dashboard">
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
</Step>
<Step title="Click on the 'Add Dynamic Secret' button">
![Add Dynamic Secret Button](../../../images/platform/dynamic-secrets/add-dynamic-secret-button.png)
</Step>
<Step title="Select `SQL Database`">
![Dynamic Secret Modal](../../../images/platform/dynamic-secrets/dynamic-secret-modal.png)
</Step>
<Step title="Provide the inputs for dynamic secret parameters">
<ParamField path="Secret Name" type="string" required>
Name by which you want the secret to be referenced
</ParamField>
<ParamField path="Default TTL" type="string" required>
Default time-to-live for a generated secret (it is possible to modify this value when a secret is generate)
</ParamField>
<ParamField path="Max TTL" type="string" required>
Maximum time-to-live for a generated secret
</ParamField>
<ParamField path="Service" type="string" required>
Choose the service you want to generate dynamic secrets for. This must be selected as **Oracle**.
</ParamField>
<ParamField path="Host" type="string" required>
Database host
</ParamField>
<ParamField path="Port" type="number" required>
Database port
</ParamField>
<ParamField path="User" type="string" required>
Username that will be used to create dynamic secrets
</ParamField>
<ParamField path="Password" type="string" required>
Password that will be used to create dynamic secrets
</ParamField>
<ParamField path="Database Name" type="string" required>
Name of the database for which you want to create dynamic secrets
</ParamField>
<ParamField path="CA(SSL)" type="string">
A CA may be required if your DB requires it for incoming connections. AWS RDS instances with default settings will requires a CA which can be downloaded [here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions).
</ParamField>
![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-modal-oracle.png)
</Step>
<Step title="(Optional) Modify SQL Statements">
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
</Step>
<Step title="Click 'Submit'">
After submitting the form, you will see a dynamic secret created in the dashboard.
<Note>
If this step fails, you may have to add the CA certficate.
</Note>
![Dynamic Secret](../../../images/platform/dynamic-secrets/dynamic-secret.png)
</Step>
<Step title="Generate dynamic secrets">
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-generate.png)
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-lease-empty.png)
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
![Provision Lease](/images/platform/dynamic-secrets/provision-lease.png)
<Tip>
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
</Tip>
Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you.
![Provision Lease](/images/platform/dynamic-secrets/lease-values.png)
</Step>
</Steps>
## Audit or Revoke Leases
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
This will allow you see the expiration time of the lease or delete a lease before it's set time to live.
![Provision Lease](/images/platform/dynamic-secrets/lease-data.png)
## Renew Leases
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** as illustrated below.
![Provision Lease](/images/platform/dynamic-secrets/dynamic-secret-lease-renew.png)
<Warning>
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
</Warning>

View File

@ -1,14 +1,13 @@
---
title: "PostgreSQL"
description: "Learn how to dynamically generate PostgreSQL Database user passwords."
description: "How to dynamically generate PostgreSQL database users"
---
The Infisical PostgreSQL secret rotation allows you to automatically rotate your PostgreSQL database user's password at a predefined interval.
The Infisical PostgreSQL dynamic secret allows you to generate PostgreSQL database credentials on demand based on configured role.
## Prerequisite
1. Create a user with the required permission in your SQL instance.
Create a user with the required permission in your SQL instance. This user will be used to create new accounts on-demand.
## Set up Dynamic Secrets with PostgreSQL
@ -17,7 +16,7 @@ The Infisical PostgreSQL secret rotation allows you to automatically rotate your
<Step title="Open Secret Overview Dashboard">
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
</Step>
<Step title="Click on the `Add Dynamic Secret` button">
<Step title="Click on the 'Add Dynamic Secret' button">
![Add Dynamic Secret Button](../../../images/platform/dynamic-secrets/add-dynamic-secret-button.png)
</Step>
<Step title="Select `SQL Database`">
@ -37,7 +36,7 @@ The Infisical PostgreSQL secret rotation allows you to automatically rotate your
</ParamField>
<ParamField path="Service" type="string" required>
Choose the service you want to generate dynamic secrets for
Choose the service you want to generate dynamic secrets for. This must be selected as **PostgreSQL**.
</ParamField>
<ParamField path="Host" type="string" required>
@ -68,31 +67,52 @@ The Infisical PostgreSQL secret rotation allows you to automatically rotate your
</Step>
<Step title="(Optional) Modify SQL Statements">
If you want to provide specific privileges for the future generated dynamic secrets, you are able to specify them as SQL statements.
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sql-statements.png)
</Step>
<Step title="Click `Submit`">
After submitting the form, you will see a dynamic secret creates in the dashboard.
<Step title="Click 'Submit'">
After submitting the form, you will see a dynamic secret created in the dashboard.
<Note>
If this step fails, you might have to add the CA certficate.
If this step fails, you may have to add the CA certficate.
</Note>
![Dynamic Secret](../../../images/platform/dynamic-secrets/dynamic-secret.png)
</Step>
<Step title="Generate dynamic secrets">
Now that the dynamic secret is created, you can start generating unique secret values by specifying the Time-to-live within the predefined range.
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
![Provision Lease](../../../images/platform/dynamic-secrets/provision-lease.png)
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-generate.png)
![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-lease-empty.png)
After you click the `Submit` button, a new secret lease will be generated and the Database User and Database Password will be shown.
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
![Provision Lease](../../../images/platform/dynamic-secrets/lease-values.png)
</Step>
<Step title="Audit or Revoke Leases">
As soon as you have generated a few secret leases, you will be able to access them by clicking `Generate` on the dynamic secret row. In this modal, you are able to see the expiration time or delete a secret preemptively.
![Provision Lease](/images/platform/dynamic-secrets/provision-lease.png)
![Provision Lease](../../../images/platform/dynamic-secrets/lease-data.png)
<Tip>
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
</Tip>
Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you.
![Provision Lease](/images/platform/dynamic-secrets/lease-values.png)
</Step>
</Steps>
## Audit or Revoke Leases
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
This will allow you see the expiration time of the lease or delete the lease before it's set time to live.
![Provision Lease](/images/platform/dynamic-secrets/lease-data.png)
## Renew Leases
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** as illustrated below.
![Provision Lease](/images/platform/dynamic-secrets/dynamic-secret-lease-renew.png)
<Warning>
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
</Warning>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -29,7 +29,8 @@ Prerequisites:
"ssm:PutParameter",
"ssm:DeleteParameter",
"ssm:GetParametersByPath",
"ssm:DeleteParameters"
"ssm:DeleteParameters",
"ssm:AddTagsToResource"
],
"Resource": "*"
}

View File

@ -28,7 +28,8 @@ Prerequisites:
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret"
"secretsmanager:UpdateSecret",
"secretsmanager:TagResource"
],
"Resource": "*"
}

View File

@ -32,10 +32,7 @@
"thumbsRating": true
},
"api": {
"baseUrl": [
"https://app.infisical.com",
"http://localhost:8080"
]
"baseUrl": ["https://app.infisical.com", "http://localhost:8080"]
},
"topbarLinks": [
{
@ -142,7 +139,9 @@
"group": "Dynamic Secrets",
"pages": [
"documentation/platform/dynamic-secrets/overview",
"documentation/platform/dynamic-secrets/postgresql"
"documentation/platform/dynamic-secrets/postgresql",
"documentation/platform/dynamic-secrets/mysql",
"documentation/platform/dynamic-secrets/oracle"
]
},
"documentation/platform/groups"
@ -370,15 +369,11 @@
},
{
"group": "Build Tool Integrations",
"pages": [
"integrations/build-tools/gradle"
]
"pages": ["integrations/build-tools/gradle"]
},
{
"group": "",
"pages": [
"sdks/overview"
]
"pages": ["sdks/overview"]
},
{
"group": "SDK's",
@ -396,22 +391,13 @@
"api-reference/overview/authentication",
{
"group": "Examples",
"pages": [
"api-reference/overview/examples/integration"
]
"pages": ["api-reference/overview/examples/integration"]
}
]
},
{
"group": "Endpoints",
"pages": [
{
"group": "Users",
"pages": [
"api-reference/endpoints/users/me",
"api-reference/endpoints/users/my-organizations"
]
},
{
"group": "Identities",
"pages": [
@ -457,7 +443,6 @@
"api-reference/endpoints/workspaces/list-identity-memberships",
"api-reference/endpoints/workspaces/update-identity-membership",
"api-reference/endpoints/workspaces/delete-identity-membership",
"api-reference/endpoints/workspaces/workspace-key",
"api-reference/endpoints/workspaces/secret-snapshots",
"api-reference/endpoints/workspaces/rollback-snapshot"
]
@ -535,15 +520,11 @@
},
{
"group": "Service Tokens",
"pages": [
"api-reference/endpoints/service-tokens/get"
]
"pages": ["api-reference/endpoints/service-tokens/get"]
},
{
"group": "Audit Logs",
"pages": [
"api-reference/endpoints/audit-logs/export-audit-log"
]
"pages": ["api-reference/endpoints/audit-logs/export-audit-log"]
}
]
},
@ -559,9 +540,7 @@
},
{
"group": "",
"pages": [
"changelog/overview"
]
"pages": ["changelog/overview"]
},
{
"group": "Contributing",
@ -585,9 +564,7 @@
},
{
"group": "Contributing to SDK",
"pages": [
"contributing/sdk/developing"
]
"pages": ["contributing/sdk/developing"]
}
]
}

View File

@ -143,8 +143,8 @@ Without email configuration, Infisical's core functions like sign-up/login and s
SMTP_HOST=email-smtp.ap-northeast-1.amazonaws.com # SMTP endpoint obtained from SMTP settings
SMTP_USERNAME=xxx # your SMTP username
SMTP_PASSWORD=xxx # your SMTP password
SMTP_PORT=587
SMTP_SECURE=false
SMTP_PORT=465
SMTP_SECURE=true
SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out emails
SMTP_FROM_NAME=Infisical
```

View File

@ -21,7 +21,8 @@ export enum DynamicSecretProviders {
export enum SqlProviders {
Postgres = "postgres",
MySql = "mysql2"
MySql = "mysql2",
Oracle = "oracledb"
}
export type TDynamicSecretProvider = {

View File

@ -63,6 +63,10 @@ export const useCreateIntegration = () => {
secretSuffix?: string;
initialSyncBehavior?: string;
shouldAutoRedeploy?: boolean;
secretAWSTag?: {
key: string;
value: string;
};
};
}) => {
const {

View File

@ -10,6 +10,7 @@ import {
faCircleInfo
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import queryString from "query-string";
import { useCreateIntegration } from "@app/hooks/api";
@ -21,11 +22,21 @@ import {
FormControl,
Input,
Select,
SelectItem
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs
} from "../../../components/v2";
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
enum TabSections {
Connection = "connection",
Options = "options"
}
const awsRegions = [
{ name: "US East (Ohio)", slug: "us-east-2" },
{ name: "US East (N. Virginia)", slug: "us-east-1" },
@ -76,6 +87,9 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [pathErrorText, setPathErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false);
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
useEffect(() => {
if (workspace) {
@ -110,7 +124,17 @@ export default function AWSParameterStoreCreateIntegrationPage() {
sourceEnvironment: selectedSourceEnvironment,
path,
region: selectedAWSRegion,
secretPath
secretPath,
metadata: {
...(shouldTag
? {
secretAWSTag: {
key: tagKey,
value: tagValue
}
}
: {})
}
});
setIsLoading(false);
@ -157,56 +181,114 @@ export default function AWSParameterStoreCreateIntegrationPage() {
</Link>
</div>
</CardTitle>
<FormControl label="Project Environment" className="px-6">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
<Tabs defaultValue={TabSections.Connection} className="px-6">
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Connection}>Connection</Tab>
<Tab value={TabSections.Options}>Options</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Connection}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Project Environment">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="Path"
errorText={pathErrorText}
isError={pathErrorText !== "" ?? false}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path" className="px-6">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region" className="px-6">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="Path"
errorText={pathErrorText}
isError={pathErrorText !== "" ?? false}
className="px-6"
>
<Input
placeholder={`/${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}/`}
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</FormControl>
<Input
placeholder={`/${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}/`}
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</FormControl>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.Options}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mt-2 ml-1">
<Switch
id="tag-aws"
onCheckedChange={() => setShouldTag(!shouldTag)}
isChecked={shouldTag}
>
Tag in AWS Parameter Store
</Switch>
</div>
{shouldTag && (
<div className="mt-4">
<FormControl
label="Tag Key"
>
<Input
placeholder="managed-by"
value={tagKey}
onChange={(e) => setTagKey(e.target.value)}
/>
</FormControl>
<FormControl
label="Tag Value"
>
<Input
placeholder="managed-by"
value={tagValue}
onChange={(e) => setTagValue(e.target.value)}
/>
</FormControl>
</div>
)}
</motion.div>
</TabPanel>
</Tabs>
<Button
onClick={handleButtonClick}
color="mineshaft"

View File

@ -10,6 +10,7 @@ import {
faCircleInfo
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import queryString from "query-string";
import { useCreateIntegration } from "@app/hooks/api";
@ -21,11 +22,21 @@ import {
FormControl,
Input,
Select,
SelectItem
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs
} from "../../../components/v2";
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
enum TabSections {
Connection = "connection",
Options = "options"
}
const awsRegions = [
{ name: "US East (Ohio)", slug: "us-east-2" },
{ name: "US East (N. Virginia)", slug: "us-east-1" },
@ -74,11 +85,14 @@ export default function AWSSecretManagerCreateIntegrationPage() {
const [selectedAWSRegion, setSelectedAWSRegion] = useState("");
const [targetSecretName, setTargetSecretName] = useState("");
const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState("");
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
// const [path, setPath] = useState('');
// const [pathErrorText, setPathErrorText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false);
useEffect(() => {
if (workspace) {
@ -109,7 +123,17 @@ export default function AWSSecretManagerCreateIntegrationPage() {
app: targetSecretName.trim(),
sourceEnvironment: selectedSourceEnvironment,
region: selectedAWSRegion,
secretPath
secretPath,
metadata: {
...(shouldTag
? {
secretAWSTag: {
key: tagKey,
value: tagValue
}
}
: {})
}
});
setIsLoading(false);
@ -156,56 +180,114 @@ export default function AWSSecretManagerCreateIntegrationPage() {
</Link>
</div>
</CardTitle>
<FormControl label="Project Environment" className="px-6">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
<Tabs defaultValue={TabSections.Connection} className="px-6">
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Connection}>Connection</Tab>
<Tab value={TabSections.Options}>Options</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Connection}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Project Environment">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="AWS SM Secret Name"
errorText={targetSecretNameErrorText}
isError={targetSecretNameErrorText !== "" ?? false}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path" className="px-6">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region" className="px-6">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="AWS SM Secret Name"
errorText={targetSecretNameErrorText}
isError={targetSecretNameErrorText !== "" ?? false}
className="px-6"
>
<Input
placeholder={`${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
value={targetSecretName}
onChange={(e) => setTargetSecretName(e.target.value)}
/>
</FormControl>
<Input
placeholder={`${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
value={targetSecretName}
onChange={(e) => setTargetSecretName(e.target.value)}
/>
</FormControl>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.Options}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mt-2 ml-1">
<Switch
id="tag-aws"
onCheckedChange={() => setShouldTag(!shouldTag)}
isChecked={shouldTag}
>
Tag in AWS Secrets Manager
</Switch>
</div>
{shouldTag && (
<div className="mt-4">
<FormControl
label="Tag Key"
>
<Input
placeholder="managed-by"
value={tagKey}
onChange={(e) => setTagKey(e.target.value)}
/>
</FormControl>
<FormControl
label="Tag Value"
>
<Input
placeholder="managed-by"
value={tagValue}
onChange={(e) => setTagValue(e.target.value)}
/>
</FormControl>
</div>
)}
</motion.div>
</TabPanel>
</Tabs>
<Button
onClick={handleButtonClick}
color="mineshaft"

View File

@ -77,6 +77,16 @@ const getSqlStatements = (provider: SqlProviders) => {
};
}
if (provider === SqlProviders.Oracle) {
return {
creationStatement:
'CREATE USER "{{username}}" IDENTIFIED BY "{{password}}";\nGRANT CONNECT TO "{{username}}";\nGRANT CREATE SESSION TO "{{username}}";',
renewStatement: "",
revocationStatement:
'REVOKE CONNECT FROM "{{username}}";\nREVOKE CREATE SESSION FROM "{{username}}";\nDROP USER "{{username}}";'
};
}
return {
creationStatement:
"CREATE USER \"{{username}}\" WITH ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";",
@ -208,6 +218,7 @@ export const SqlDatabaseInputForm = ({
>
<SelectItem value={SqlProviders.Postgres}>PostgreSQL</SelectItem>
<SelectItem value={SqlProviders.MySql}>MySQL</SelectItem>
<SelectItem value={SqlProviders.Oracle}>Oracle</SelectItem>
</Select>
</FormControl>
)}

View File

@ -193,6 +193,7 @@ export const EditDynamicSecretSqlProviderForm = ({
>
<SelectItem value={SqlProviders.Postgres}>PostgreSQL</SelectItem>
<SelectItem value={SqlProviders.MySql}>MySQL</SelectItem>
<SelectItem value={SqlProviders.Oracle}>Oracle</SelectItem>
</Select>
</FormControl>
)}

View File

@ -5,4 +5,8 @@ export type Metadata = {
labelName: string;
labelValue: string;
}
secretAWSTag?: {
key: string;
value: string;
}
}