mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-02 16:55:02 +00:00
Compare commits
47 Commits
daniel/bet
...
daniel/bet
Author | SHA1 | Date | |
---|---|---|---|
bd4deb02b0 | |||
2a686e65cd | |||
e44213a8a9 | |||
aee46d1902 | |||
279a1791f6 | |||
c50e325f53 | |||
0225e6fabb | |||
3caa46ade8 | |||
36adc5e00e | |||
cb24b2aac8 | |||
1e0eb26dce | |||
f8161c8c72 | |||
862e2e9d65 | |||
0e734bd638 | |||
a35054f6ba | |||
e0ace85d6e | |||
7867587884 | |||
8ace72d134 | |||
491331e9e3 | |||
4a324eafd8 | |||
173cf0238d | |||
fd792e7e1d | |||
d6b7045461 | |||
bd9c9ea1f4 | |||
d4c95ab1a7 | |||
03c4c2056a | |||
6d9f80805e | |||
5740d2b4e4 | |||
09887a7405 | |||
38ee3a005e | |||
74653e7ed1 | |||
8a0b1bb427 | |||
1f6faadf81 | |||
8f3b7e1698 | |||
24c460c695 | |||
8acceab1e7 | |||
d60aba9339 | |||
3a228f7521 | |||
3f7ac0f142 | |||
63cf535ebb | |||
69a2a46c47 | |||
d081077273 | |||
75034f9350 | |||
eacd7b0c6a | |||
5bad77083c | |||
1025759efb | |||
5e5ab29ab9 |
417
backend/package-lock.json
generated
417
backend/package-lock.json
generated
@ -81,7 +81,7 @@
|
|||||||
"pino": "^8.16.2",
|
"pino": "^8.16.2",
|
||||||
"pkijs": "^3.2.4",
|
"pkijs": "^3.2.4",
|
||||||
"posthog-node": "^3.6.2",
|
"posthog-node": "^3.6.2",
|
||||||
"probot": "^13.0.0",
|
"probot": "^13.3.8",
|
||||||
"safe-regex": "^2.1.1",
|
"safe-regex": "^2.1.1",
|
||||||
"scim-patch": "^0.8.3",
|
"scim-patch": "^0.8.3",
|
||||||
"scim2-parse-filter": "^0.2.10",
|
"scim2-parse-filter": "^0.2.10",
|
||||||
@ -8018,6 +8018,7 @@
|
|||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-types": "~2.1.34",
|
"mime-types": "~2.1.34",
|
||||||
"negotiator": "0.6.3"
|
"negotiator": "0.6.3"
|
||||||
@ -8336,7 +8337,8 @@
|
|||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/array-includes": {
|
"node_modules/array-includes": {
|
||||||
"version": "3.1.7",
|
"version": "3.1.7",
|
||||||
@ -8814,9 +8816,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"content-type": "~1.0.5",
|
"content-type": "~1.0.5",
|
||||||
@ -8826,7 +8829,7 @@
|
|||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"iconv-lite": "0.4.24",
|
"iconv-lite": "0.4.24",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"raw-body": "2.5.2",
|
"raw-body": "2.5.2",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
@ -8840,6 +8843,7 @@
|
|||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@ -8848,6 +8852,7 @@
|
|||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
},
|
},
|
||||||
@ -8858,7 +8863,8 @@
|
|||||||
"node_modules/body-parser/node_modules/ms": {
|
"node_modules/body-parser/node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/bottleneck": {
|
"node_modules/bottleneck": {
|
||||||
"version": "2.19.5",
|
"version": "2.19.5",
|
||||||
@ -9006,6 +9012,7 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@ -9028,13 +9035,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||||
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"es-define-property": "^1.0.0",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
"get-intrinsic": "^1.2.1",
|
"get-intrinsic": "^1.2.4",
|
||||||
"set-function-length": "^1.1.1"
|
"set-function-length": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@ -9379,6 +9392,7 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -9543,16 +9557,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/define-data-property": {
|
"node_modules/define-data-property": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.1",
|
"es-define-property": "^1.0.0",
|
||||||
"gopd": "^1.0.1",
|
"es-errors": "^1.3.0",
|
||||||
"has-property-descriptors": "^1.0.0"
|
"gopd": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/define-lazy-prop": {
|
"node_modules/define-lazy-prop": {
|
||||||
@ -9618,6 +9636,7 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8",
|
"node": ">= 0.8",
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
@ -9724,7 +9743,8 @@
|
|||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.816",
|
"version": "1.4.816",
|
||||||
@ -9738,9 +9758,10 @@
|
|||||||
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
|
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@ -9827,6 +9848,27 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.4"
|
||||||
|
},
|
||||||
|
"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-set-tostringtag": {
|
"node_modules/es-set-tostringtag": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
|
||||||
@ -10452,6 +10494,7 @@
|
|||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -10495,36 +10538,37 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.19.2",
|
"version": "4.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.3",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.6.0",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"etag": "~1.8.1",
|
"etag": "~1.8.1",
|
||||||
"finalhandler": "1.2.0",
|
"finalhandler": "1.3.1",
|
||||||
"fresh": "0.5.2",
|
"fresh": "0.5.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"merge-descriptors": "1.0.1",
|
"merge-descriptors": "1.0.3",
|
||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.7",
|
"path-to-regexp": "0.1.10",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"send": "0.18.0",
|
"send": "0.19.0",
|
||||||
"serve-static": "1.15.0",
|
"serve-static": "1.16.2",
|
||||||
"setprototypeof": "1.2.0",
|
"setprototypeof": "1.2.0",
|
||||||
"statuses": "2.0.1",
|
"statuses": "2.0.1",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
@ -10588,6 +10632,7 @@
|
|||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -10595,12 +10640,14 @@
|
|||||||
"node_modules/express/node_modules/cookie-signature": {
|
"node_modules/express/node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/express/node_modules/debug": {
|
"node_modules/express/node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@ -10608,7 +10655,8 @@
|
|||||||
"node_modules/express/node_modules/ms": {
|
"node_modules/express/node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/extend": {
|
"node_modules/extend": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
@ -10815,12 +10863,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "1.2.0",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
@ -10835,6 +10884,7 @@
|
|||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@ -10842,7 +10892,8 @@
|
|||||||
"node_modules/finalhandler/node_modules/ms": {
|
"node_modules/finalhandler/node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/find-my-way": {
|
"node_modules/find-my-way": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
@ -11008,6 +11059,7 @@
|
|||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -11365,15 +11417,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||||
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
"has-proto": "^1.0.1",
|
"has-proto": "^1.0.1",
|
||||||
"has-symbols": "^1.0.3",
|
"has-symbols": "^1.0.3",
|
||||||
"hasown": "^2.0.0"
|
"hasown": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@ -11719,11 +11776,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-property-descriptors": {
|
"node_modules/has-property-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.2"
|
"es-define-property": "^1.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@ -13276,6 +13334,7 @@
|
|||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -13286,9 +13345,13 @@
|
|||||||
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
|
||||||
},
|
},
|
||||||
"node_modules/merge-descriptors": {
|
"node_modules/merge-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -13309,6 +13372,7 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -13748,6 +13812,7 @@
|
|||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -14099,6 +14164,7 @@
|
|||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ee-first": "1.1.1"
|
"ee-first": "1.1.1"
|
||||||
},
|
},
|
||||||
@ -14511,9 +14577,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -14716,20 +14783,78 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pino-http": {
|
"node_modules/pino-http": {
|
||||||
"version": "8.6.1",
|
"version": "10.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/pino-http/-/pino-http-10.3.0.tgz",
|
||||||
"integrity": "sha512-J0hiJgUExtBXP2BjrK4VB305tHXS31sCmWJ9XJo2wPkLHa1NFPuW4V9wjG27PAc2fmBCigiNhQKpvrx+kntBPA==",
|
"integrity": "sha512-kaHQqt1i5S9LXWmyuw6aPPqYW/TjoDPizPs4PnDW4hSpajz2Uo/oisNliLf7We1xzpiLacdntmw8yaZiEkppQQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-caller-file": "^2.0.5",
|
"get-caller-file": "^2.0.5",
|
||||||
"pino": "^8.17.1",
|
"pino": "^9.0.0",
|
||||||
"pino-std-serializers": "^6.2.2",
|
"pino-std-serializers": "^7.0.0",
|
||||||
"process-warning": "^3.0.0"
|
"process-warning": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pino-http/node_modules/pino": {
|
||||||
|
"version": "9.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz",
|
||||||
|
"integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0",
|
||||||
|
"fast-redact": "^3.1.1",
|
||||||
|
"on-exit-leak-free": "^2.1.0",
|
||||||
|
"pino-abstract-transport": "^1.2.0",
|
||||||
|
"pino-std-serializers": "^7.0.0",
|
||||||
|
"process-warning": "^4.0.0",
|
||||||
|
"quick-format-unescaped": "^4.0.3",
|
||||||
|
"real-require": "^0.2.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"sonic-boom": "^4.0.1",
|
||||||
|
"thread-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pino": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-http/node_modules/pino-abstract-transport": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^4.0.0",
|
||||||
|
"split2": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-http/node_modules/pino-std-serializers": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/pino-http/node_modules/process-warning": {
|
"node_modules/pino-http/node_modules/process-warning": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz",
|
||||||
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
|
"integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pino-http/node_modules/sonic-boom": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-http/node_modules/thread-stream": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"real-require": "^0.2.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pino-pretty": {
|
"node_modules/pino-pretty": {
|
||||||
"version": "10.2.3",
|
"version": "10.2.3",
|
||||||
@ -15096,9 +15221,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/probot": {
|
"node_modules/probot": {
|
||||||
"version": "13.0.0",
|
"version": "13.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/probot/-/probot-13.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/probot/-/probot-13.3.8.tgz",
|
||||||
"integrity": "sha512-3ht9kAJ+ISjLyWLLCKVdrLE5xs/x+zUx07J5kYTxAyIxUvwF6Acr8xT5fiNihbBHAsEl4+A4CMYZQvZ5hx5bgw==",
|
"integrity": "sha512-xc+KBC0mp1JKFMsPbMyj1SpmN0B7Q8uFO7ze4PBbNv74q8AyPGqYL3TmkZSOmcOjFTeFrZTnMYEoXi+z1anyLA==",
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/core": "^5.0.2",
|
"@octokit/core": "^5.0.2",
|
||||||
"@octokit/plugin-enterprise-compatibility": "^4.0.1",
|
"@octokit/plugin-enterprise-compatibility": "^4.0.1",
|
||||||
@ -15113,19 +15239,18 @@
|
|||||||
"@probot/octokit-plugin-config": "^2.0.1",
|
"@probot/octokit-plugin-config": "^2.0.1",
|
||||||
"@probot/pino": "^2.3.5",
|
"@probot/pino": "^2.3.5",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"commander": "^11.1.0",
|
"bottleneck": "^2.19.5",
|
||||||
|
"commander": "^12.0.0",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"eventsource": "^2.0.2",
|
"express": "^4.21.0",
|
||||||
"express": "^4.18.2",
|
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lru-cache": "^10.0.3",
|
"lru-cache": "^10.0.3",
|
||||||
"octokit-auth-probot": "^2.0.0",
|
"octokit-auth-probot": "^2.0.0",
|
||||||
"pino": "^8.16.1",
|
"pino": "^9.0.0",
|
||||||
"pino-http": "^8.5.1",
|
"pino-http": "^10.0.0",
|
||||||
"pkg-conf": "^3.1.0",
|
"pkg-conf": "^3.1.0",
|
||||||
"resolve": "^1.22.8",
|
|
||||||
"update-dotenv": "^1.1.1"
|
"update-dotenv": "^1.1.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -15152,11 +15277,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/probot/node_modules/commander": {
|
"node_modules/probot/node_modules/commander": {
|
||||||
"version": "11.1.0",
|
"version": "12.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||||
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/probot/node_modules/lru-cache": {
|
"node_modules/probot/node_modules/lru-cache": {
|
||||||
@ -15167,6 +15293,68 @@
|
|||||||
"node": "14 || >=16.14"
|
"node": "14 || >=16.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/probot/node_modules/pino": {
|
||||||
|
"version": "9.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz",
|
||||||
|
"integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0",
|
||||||
|
"fast-redact": "^3.1.1",
|
||||||
|
"on-exit-leak-free": "^2.1.0",
|
||||||
|
"pino-abstract-transport": "^1.2.0",
|
||||||
|
"pino-std-serializers": "^7.0.0",
|
||||||
|
"process-warning": "^4.0.0",
|
||||||
|
"quick-format-unescaped": "^4.0.3",
|
||||||
|
"real-require": "^0.2.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"sonic-boom": "^4.0.1",
|
||||||
|
"thread-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pino": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/probot/node_modules/pino-abstract-transport": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^4.0.0",
|
||||||
|
"split2": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/probot/node_modules/pino-std-serializers": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/probot/node_modules/process-warning": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/probot/node_modules/sonic-boom": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/probot/node_modules/thread-stream": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"real-require": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process": {
|
"node_modules/process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
@ -15282,11 +15470,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
@ -15359,6 +15548,7 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -15367,6 +15557,7 @@
|
|||||||
"version": "2.5.2",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
@ -15381,6 +15572,7 @@
|
|||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
},
|
},
|
||||||
@ -15961,9 +16153,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/send": {
|
"node_modules/send": {
|
||||||
"version": "0.18.0",
|
"version": "0.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
@ -15987,6 +16180,7 @@
|
|||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@ -15994,12 +16188,23 @@
|
|||||||
"node_modules/send/node_modules/debug/node_modules/ms": {
|
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/send/node_modules/encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/send/node_modules/mime": {
|
"node_modules/send/node_modules/mime": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"mime": "cli.js"
|
"mime": "cli.js"
|
||||||
},
|
},
|
||||||
@ -16013,14 +16218,15 @@
|
|||||||
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
||||||
},
|
},
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.15.0",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"send": "0.18.0"
|
"send": "0.19.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
@ -16037,14 +16243,17 @@
|
|||||||
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
||||||
},
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.1.1",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.1",
|
"define-data-property": "^1.1.4",
|
||||||
"get-intrinsic": "^1.2.1",
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-intrinsic": "^1.2.4",
|
||||||
"gopd": "^1.0.1",
|
"gopd": "^1.0.1",
|
||||||
"has-property-descriptors": "^1.0.0"
|
"has-property-descriptors": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -16103,13 +16312,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.0",
|
"call-bind": "^1.0.7",
|
||||||
"get-intrinsic": "^1.0.2",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.9.0"
|
"get-intrinsic": "^1.2.4",
|
||||||
|
"object-inspect": "^1.13.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@ -17704,6 +17918,7 @@
|
|||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"media-typer": "0.3.0",
|
"media-typer": "0.3.0",
|
||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
@ -17927,6 +18142,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@ -18051,6 +18267,7 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@
|
|||||||
"pino": "^8.16.2",
|
"pino": "^8.16.2",
|
||||||
"pkijs": "^3.2.4",
|
"pkijs": "^3.2.4",
|
||||||
"posthog-node": "^3.6.2",
|
"posthog-node": "^3.6.2",
|
||||||
"probot": "^13.0.0",
|
"probot": "^13.3.8",
|
||||||
"safe-regex": "^2.1.1",
|
"safe-regex": "^2.1.1",
|
||||||
"scim-patch": "^0.8.3",
|
"scim-patch": "^0.8.3",
|
||||||
"scim2-parse-filter": "^0.2.10",
|
"scim2-parse-filter": "^0.2.10",
|
||||||
|
@ -146,12 +146,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
projectId: req.params.workspaceId,
|
actor: req.permission.type,
|
||||||
...req.query,
|
|
||||||
endDate: req.query.endDate,
|
filter: {
|
||||||
startDate: req.query.startDate || getLastMidnightDateISO(),
|
...req.query,
|
||||||
auditLogActor: req.query.actor,
|
projectId: req.params.workspaceId,
|
||||||
actor: req.permission.type
|
endDate: req.query.endDate,
|
||||||
|
startDate: req.query.startDate || getLastMidnightDateISO(),
|
||||||
|
auditLogActorId: req.query.actor,
|
||||||
|
eventType: req.query.eventType ? [req.query.eventType] : undefined
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { auditLogs };
|
return { auditLogs };
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ import { DatabaseError } from "@app/lib/errors";
|
|||||||
import { ormify, selectAllTableCols, stripUndefinedInWhere } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, stripUndefinedInWhere } from "@app/lib/knex";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueName } from "@app/queue";
|
import { QueueName } from "@app/queue";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { EventType } from "./audit-log-types";
|
||||||
|
|
||||||
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
||||||
|
|
||||||
@ -25,7 +28,24 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
const auditLogOrm = ormify(db, TableName.AuditLog);
|
const auditLogOrm = ormify(db, TableName.AuditLog);
|
||||||
|
|
||||||
const find = async (
|
const find = async (
|
||||||
{ orgId, projectId, userAgentType, startDate, endDate, limit = 20, offset = 0, actor, eventType }: TFindQuery,
|
{
|
||||||
|
orgId,
|
||||||
|
projectId,
|
||||||
|
userAgentType,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
limit = 20,
|
||||||
|
offset = 0,
|
||||||
|
actorId,
|
||||||
|
actorType,
|
||||||
|
eventType,
|
||||||
|
eventMetadata
|
||||||
|
}: Omit<TFindQuery, "actor" | "eventType"> & {
|
||||||
|
actorId?: string;
|
||||||
|
actorType?: ActorType;
|
||||||
|
eventType?: EventType[];
|
||||||
|
eventMetadata?: Record<string, string>;
|
||||||
|
},
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@ -34,7 +54,6 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
stripUndefinedInWhere({
|
stripUndefinedInWhere({
|
||||||
projectId,
|
projectId,
|
||||||
[`${TableName.AuditLog}.orgId`]: orgId,
|
[`${TableName.AuditLog}.orgId`]: orgId,
|
||||||
eventType,
|
|
||||||
userAgentType
|
userAgentType
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -52,8 +71,22 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
.offset(offset)
|
.offset(offset)
|
||||||
.orderBy(`${TableName.AuditLog}.createdAt`, "desc");
|
.orderBy(`${TableName.AuditLog}.createdAt`, "desc");
|
||||||
|
|
||||||
if (actor) {
|
if (actorId) {
|
||||||
void sqlQuery.whereRaw(`"actorMetadata"->>'userId' = ?`, [actor]);
|
void sqlQuery.whereRaw(`"actorMetadata"->>'userId' = ?`, [actorId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventMetadata && Object.keys(eventMetadata).length) {
|
||||||
|
Object.entries(eventMetadata).forEach(([key, value]) => {
|
||||||
|
void sqlQuery.whereRaw(`"eventMetadata"->>'${key}' = ?`, [value]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actorType) {
|
||||||
|
void sqlQuery.where("actor", actorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType?.length) {
|
||||||
|
void sqlQuery.whereIn("eventType", eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
|
@ -23,25 +23,12 @@ export const auditLogServiceFactory = ({
|
|||||||
auditLogQueue,
|
auditLogQueue,
|
||||||
permissionService
|
permissionService
|
||||||
}: TAuditLogServiceFactoryDep) => {
|
}: TAuditLogServiceFactoryDep) => {
|
||||||
const listAuditLogs = async ({
|
const listAuditLogs = async ({ actorAuthMethod, actorId, actorOrgId, actor, filter }: TListProjectAuditLogDTO) => {
|
||||||
userAgentType,
|
if (filter.projectId) {
|
||||||
eventType,
|
|
||||||
offset,
|
|
||||||
limit,
|
|
||||||
endDate,
|
|
||||||
startDate,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectId,
|
|
||||||
auditLogActor
|
|
||||||
}: TListProjectAuditLogDTO) => {
|
|
||||||
if (projectId) {
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
filter.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
@ -65,14 +52,16 @@ export const auditLogServiceFactory = ({
|
|||||||
// If project ID is not provided, then we need to return all the audit logs for the organization itself.
|
// If project ID is not provided, then we need to return all the audit logs for the organization itself.
|
||||||
|
|
||||||
const auditLogs = await auditLogDAL.find({
|
const auditLogs = await auditLogDAL.find({
|
||||||
startDate,
|
startDate: filter.startDate,
|
||||||
endDate,
|
endDate: filter.endDate,
|
||||||
limit,
|
limit: filter.limit,
|
||||||
offset,
|
offset: filter.offset,
|
||||||
eventType,
|
eventType: filter.eventType,
|
||||||
userAgentType,
|
userAgentType: filter.userAgentType,
|
||||||
actor: auditLogActor,
|
actorId: filter.auditLogActorId,
|
||||||
...(projectId ? { projectId } : { orgId: actorOrgId })
|
actorType: filter.actorType,
|
||||||
|
eventMetadata: filter.eventMetadata,
|
||||||
|
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
|
||||||
});
|
});
|
||||||
|
|
||||||
return auditLogs.map(({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({
|
return auditLogs.map(({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({
|
||||||
|
@ -5,19 +5,23 @@ import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
|||||||
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
||||||
|
|
||||||
export type TListProjectAuditLogDTO = {
|
export type TListProjectAuditLogDTO = {
|
||||||
auditLogActor?: string;
|
filter: {
|
||||||
projectId?: string;
|
userAgentType?: UserAgentType;
|
||||||
eventType?: string;
|
eventType?: EventType[];
|
||||||
startDate?: string;
|
offset?: number;
|
||||||
endDate?: string;
|
limit: number;
|
||||||
userAgentType?: string;
|
endDate?: string;
|
||||||
limit?: number;
|
startDate?: string;
|
||||||
offset?: number;
|
projectId?: string;
|
||||||
|
auditLogActorId?: string;
|
||||||
|
actorType?: ActorType;
|
||||||
|
eventMetadata?: Record<string, string>;
|
||||||
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TCreateAuditLogDTO = {
|
export type TCreateAuditLogDTO = {
|
||||||
event: Event;
|
event: Event;
|
||||||
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor;
|
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor;
|
||||||
orgId?: string;
|
orgId?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
} & BaseAuthData;
|
} & BaseAuthData;
|
||||||
@ -177,7 +181,8 @@ export enum EventType {
|
|||||||
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
|
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
|
||||||
DELETE_SLACK_INTEGRATION = "delete-slack-integration",
|
DELETE_SLACK_INTEGRATION = "delete-slack-integration",
|
||||||
GET_PROJECT_SLACK_CONFIG = "get-project-slack-config",
|
GET_PROJECT_SLACK_CONFIG = "get-project-slack-config",
|
||||||
UPDATE_PROJECT_SLACK_CONFIG = "update-project-slack-config"
|
UPDATE_PROJECT_SLACK_CONFIG = "update-project-slack-config",
|
||||||
|
INTEGRATION_SYNCED = "integration-synced"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserActorMetadata {
|
interface UserActorMetadata {
|
||||||
@ -198,6 +203,8 @@ interface IdentityActorMetadata {
|
|||||||
|
|
||||||
interface ScimClientActorMetadata {}
|
interface ScimClientActorMetadata {}
|
||||||
|
|
||||||
|
interface PlatformActorMetadata {}
|
||||||
|
|
||||||
export interface UserActor {
|
export interface UserActor {
|
||||||
type: ActorType.USER;
|
type: ActorType.USER;
|
||||||
metadata: UserActorMetadata;
|
metadata: UserActorMetadata;
|
||||||
@ -208,6 +215,11 @@ export interface ServiceActor {
|
|||||||
metadata: ServiceActorMetadata;
|
metadata: ServiceActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlatformActor {
|
||||||
|
type: ActorType.PLATFORM;
|
||||||
|
metadata: PlatformActorMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IdentityActor {
|
export interface IdentityActor {
|
||||||
type: ActorType.IDENTITY;
|
type: ActorType.IDENTITY;
|
||||||
metadata: IdentityActorMetadata;
|
metadata: IdentityActorMetadata;
|
||||||
@ -218,7 +230,7 @@ export interface ScimClientActor {
|
|||||||
metadata: ScimClientActorMetadata;
|
metadata: ScimClientActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actor = UserActor | ServiceActor | IdentityActor | ScimClientActor;
|
export type Actor = UserActor | ServiceActor | IdentityActor | ScimClientActor | PlatformActor;
|
||||||
|
|
||||||
interface GetSecretsEvent {
|
interface GetSecretsEvent {
|
||||||
type: EventType.GET_SECRETS;
|
type: EventType.GET_SECRETS;
|
||||||
@ -1518,6 +1530,16 @@ interface GetProjectSlackConfig {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
interface IntegrationSyncedEvent {
|
||||||
|
type: EventType.INTEGRATION_SYNCED;
|
||||||
|
metadata: {
|
||||||
|
integrationId: string;
|
||||||
|
lastSyncJobId: string;
|
||||||
|
lastUsed: Date;
|
||||||
|
syncMessage: string;
|
||||||
|
isSynced: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
@ -1657,4 +1679,5 @@ export type Event =
|
|||||||
| DeleteSlackIntegration
|
| DeleteSlackIntegration
|
||||||
| GetSlackIntegration
|
| GetSlackIntegration
|
||||||
| UpdateProjectSlackConfig
|
| UpdateProjectSlackConfig
|
||||||
| GetProjectSlackConfig;
|
| GetProjectSlackConfig
|
||||||
|
| IntegrationSyncedEvent;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability";
|
import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability";
|
||||||
|
|
||||||
import { conditionsMatcher } from "@app/lib/casl";
|
|
||||||
|
|
||||||
export enum OrgPermissionActions {
|
export enum OrgPermissionActions {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Create = "create",
|
Create = "create",
|
||||||
@ -48,7 +46,7 @@ export type OrgPermissionSet =
|
|||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
|
||||||
|
|
||||||
const buildAdminPermission = () => {
|
const buildAdminPermission = () => {
|
||||||
const { can, build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||||
// ws permissions
|
// ws permissions
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||||
@ -115,13 +113,13 @@ const buildAdminPermission = () => {
|
|||||||
|
|
||||||
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
||||||
|
|
||||||
return build({ conditionsMatcher });
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orgAdminPermissions = buildAdminPermission();
|
export const orgAdminPermissions = buildAdminPermission();
|
||||||
|
|
||||||
const buildMemberPermission = () => {
|
const buildMemberPermission = () => {
|
||||||
const { can, build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||||
@ -142,14 +140,14 @@ const buildMemberPermission = () => {
|
|||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
return build({ conditionsMatcher });
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orgMemberPermissions = buildMemberPermission();
|
export const orgMemberPermissions = buildMemberPermission();
|
||||||
|
|
||||||
const buildNoAccessPermission = () => {
|
const buildNoAccessPermission = () => {
|
||||||
const { build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
const { rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||||
return build({ conditionsMatcher });
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orgNoAccessPermissions = buildNoAccessPermission();
|
export const orgNoAccessPermissions = buildNoAccessPermission();
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { IdentityProjectMembershipRoleSchema, ProjectUserMembershipRolesSchema, TableName } from "@app/db/schemas";
|
import {
|
||||||
|
IdentityProjectMembershipRoleSchema,
|
||||||
|
OrgMembershipsSchema,
|
||||||
|
TableName,
|
||||||
|
TProjectRoles,
|
||||||
|
TProjects
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||||
|
|
||||||
@ -10,18 +16,91 @@ export type TPermissionDALFactory = ReturnType<typeof permissionDALFactory>;
|
|||||||
export const permissionDALFactory = (db: TDbClient) => {
|
export const permissionDALFactory = (db: TDbClient) => {
|
||||||
const getOrgPermission = async (userId: string, orgId: string) => {
|
const getOrgPermission = async (userId: string, orgId: string) => {
|
||||||
try {
|
try {
|
||||||
|
const groupSubQuery = db(TableName.Groups)
|
||||||
|
.where(`${TableName.Groups}.orgId`, orgId)
|
||||||
|
.join(TableName.UserGroupMembership, (queryBuilder) => {
|
||||||
|
queryBuilder
|
||||||
|
.on(`${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
|
||||||
|
.andOn(`${TableName.UserGroupMembership}.userId`, db.raw("?", [userId]));
|
||||||
|
})
|
||||||
|
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
|
.select(
|
||||||
|
db.ref("id").withSchema(TableName.Groups).as("groupId"),
|
||||||
|
db.ref("orgId").withSchema(TableName.Groups).as("groupOrgId"),
|
||||||
|
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug"),
|
||||||
|
db.ref("role").withSchema(TableName.Groups).as("groupRole"),
|
||||||
|
db.ref("roleId").withSchema(TableName.Groups).as("groupRoleId"),
|
||||||
|
db.ref("createdAt").withSchema(TableName.Groups).as("groupCreatedAt"),
|
||||||
|
db.ref("updatedAt").withSchema(TableName.Groups).as("groupUpdatedAt"),
|
||||||
|
db.ref("permissions").withSchema(TableName.OrgRoles).as("groupCustomRolePermission")
|
||||||
|
);
|
||||||
|
|
||||||
const membership = await db
|
const membership = await db
|
||||||
.replicaNode()(TableName.OrgMembership)
|
.replicaNode()(TableName.OrgMembership)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
|
||||||
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
|
|
||||||
.where("userId", userId)
|
|
||||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||||
.select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"))
|
.where(`${TableName.OrgMembership}.userId`, userId)
|
||||||
.select("permissions")
|
.leftJoin(TableName.OrgRoles, `${TableName.OrgRoles}.id`, `${TableName.OrgMembership}.roleId`)
|
||||||
.select(selectAllTableCols(TableName.OrgMembership))
|
.leftJoin<Awaited<typeof groupSubQuery>[0]>(
|
||||||
.first();
|
groupSubQuery.as("userGroups"),
|
||||||
|
"userGroups.groupOrgId",
|
||||||
|
db.raw("?", [orgId])
|
||||||
|
)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
||||||
|
.select(
|
||||||
|
selectAllTableCols(TableName.OrgMembership),
|
||||||
|
db.ref("slug").withSchema(TableName.OrgRoles).withSchema(TableName.OrgRoles).as("customRoleSlug"),
|
||||||
|
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||||
|
db.ref("groupId").withSchema("userGroups"),
|
||||||
|
db.ref("groupOrgId").withSchema("userGroups"),
|
||||||
|
db.ref("groupName").withSchema("userGroups"),
|
||||||
|
db.ref("groupSlug").withSchema("userGroups"),
|
||||||
|
db.ref("groupRole").withSchema("userGroups"),
|
||||||
|
db.ref("groupRoleId").withSchema("userGroups"),
|
||||||
|
db.ref("groupCreatedAt").withSchema("userGroups"),
|
||||||
|
db.ref("groupUpdatedAt").withSchema("userGroups"),
|
||||||
|
db.ref("groupCustomRolePermission").withSchema("userGroups")
|
||||||
|
);
|
||||||
|
|
||||||
return membership;
|
const [formatedDoc] = sqlNestRelationships({
|
||||||
|
data: membership,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (el) =>
|
||||||
|
OrgMembershipsSchema.extend({
|
||||||
|
permissions: z.unknown(),
|
||||||
|
orgAuthEnforced: z.boolean().optional().nullable(),
|
||||||
|
customRoleSlug: z.string().optional().nullable()
|
||||||
|
}).parse(el),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "groupId",
|
||||||
|
label: "groups" as const,
|
||||||
|
mapper: ({
|
||||||
|
groupId,
|
||||||
|
groupUpdatedAt,
|
||||||
|
groupCreatedAt,
|
||||||
|
groupRole,
|
||||||
|
groupRoleId,
|
||||||
|
groupCustomRolePermission,
|
||||||
|
groupName,
|
||||||
|
groupSlug,
|
||||||
|
groupOrgId
|
||||||
|
}) => ({
|
||||||
|
id: groupId,
|
||||||
|
updatedAt: groupUpdatedAt,
|
||||||
|
createdAt: groupCreatedAt,
|
||||||
|
role: groupRole,
|
||||||
|
roleId: groupRoleId,
|
||||||
|
customRolePermission: groupCustomRolePermission,
|
||||||
|
name: groupName,
|
||||||
|
slug: groupSlug,
|
||||||
|
orgId: groupOrgId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return formatedDoc;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "GetOrgPermission" });
|
throw new DatabaseError({ error, name: "GetOrgPermission" });
|
||||||
}
|
}
|
||||||
@ -47,74 +126,31 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getProjectPermission = async (userId: string, projectId: string) => {
|
const getProjectPermission = async (userId: string, projectId: string) => {
|
||||||
try {
|
try {
|
||||||
const groups: string[] = await db
|
const docs = await db
|
||||||
.replicaNode()(TableName.GroupProjectMembership)
|
.replicaNode()(TableName.Users)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.Users}.id`, userId)
|
||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.leftJoin(TableName.UserGroupMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.GroupProjectMembership, (queryBuilder) => {
|
||||||
const groupDocs = await db
|
void queryBuilder
|
||||||
.replicaNode()(TableName.UserGroupMembership)
|
.on(`${TableName.GroupProjectMembership}.projectId`, db.raw("?", [projectId]))
|
||||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
.andOn(`${TableName.GroupProjectMembership}.groupId`, `${TableName.UserGroupMembership}.groupId`);
|
||||||
.whereIn(`${TableName.UserGroupMembership}.groupId`, groups)
|
})
|
||||||
.join(
|
.leftJoin(
|
||||||
TableName.GroupProjectMembership,
|
|
||||||
`${TableName.GroupProjectMembership}.groupId`,
|
|
||||||
`${TableName.UserGroupMembership}.groupId`
|
|
||||||
)
|
|
||||||
.join(
|
|
||||||
TableName.GroupProjectMembershipRole,
|
TableName.GroupProjectMembershipRole,
|
||||||
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
||||||
`${TableName.GroupProjectMembership}.id`
|
`${TableName.GroupProjectMembership}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin<TProjectRoles>(
|
||||||
.leftJoin(
|
{ groupCustomRoles: TableName.ProjectRoles },
|
||||||
TableName.ProjectRoles,
|
|
||||||
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
||||||
`${TableName.ProjectRoles}.id`
|
`groupCustomRoles.id`
|
||||||
)
|
)
|
||||||
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
|
.leftJoin(TableName.ProjectMembership, (queryBuilder) => {
|
||||||
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
void queryBuilder
|
||||||
|
.on(`${TableName.ProjectMembership}.projectId`, db.raw("?", [projectId]))
|
||||||
|
.andOn(`${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`);
|
||||||
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
|
||||||
`${TableName.GroupProjectMembership}.projectId`,
|
|
||||||
`${TableName.Project}.id`
|
|
||||||
)
|
|
||||||
.select(selectAllTableCols(TableName.GroupProjectMembershipRole))
|
|
||||||
.select(
|
|
||||||
db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"),
|
|
||||||
db.ref("createdAt").withSchema(TableName.GroupProjectMembership).as("membershipCreatedAt"),
|
|
||||||
db.ref("updatedAt").withSchema(TableName.GroupProjectMembership).as("membershipUpdatedAt"),
|
|
||||||
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
|
|
||||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
|
||||||
db.ref("orgId").withSchema(TableName.Project),
|
|
||||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
|
||||||
|
|
||||||
db.ref("permissions").withSchema(TableName.ProjectRoles).as("permissions"),
|
|
||||||
// db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("apPermissions")
|
|
||||||
// Additional Privileges
|
|
||||||
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
|
|
||||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"),
|
|
||||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
|
|
||||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
|
|
||||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
|
|
||||||
|
|
||||||
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"),
|
|
||||||
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"),
|
|
||||||
|
|
||||||
db
|
|
||||||
.ref("temporaryAccessStartTime")
|
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
|
||||||
.as("userApTemporaryAccessStartTime"),
|
|
||||||
db
|
|
||||||
.ref("temporaryAccessEndTime")
|
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
|
||||||
.as("userApTemporaryAccessEndTime")
|
|
||||||
);
|
|
||||||
// .select(`${TableName.ProjectRoles}.permissions`);
|
|
||||||
|
|
||||||
const docs = await db(TableName.ProjectMembership)
|
|
||||||
.join(
|
|
||||||
TableName.ProjectUserMembershipRole,
|
TableName.ProjectUserMembershipRole,
|
||||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||||
`${TableName.ProjectMembership}.id`
|
`${TableName.ProjectMembership}.id`
|
||||||
@ -124,176 +160,229 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||||
`${TableName.ProjectRoles}.id`
|
`${TableName.ProjectRoles}.id`
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(TableName.ProjectUserAdditionalPrivilege, (queryBuilder) => {
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
void queryBuilder
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.projectId`,
|
.on(`${TableName.ProjectUserAdditionalPrivilege}.projectId`, db.raw("?", [projectId]))
|
||||||
`${TableName.ProjectMembership}.projectId`
|
.andOn(`${TableName.ProjectUserAdditionalPrivilege}.userId`, `${TableName.Users}.id`);
|
||||||
)
|
})
|
||||||
|
.join<TProjects>(TableName.Project, `${TableName.Project}.id`, db.raw("?", [projectId]))
|
||||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
|
||||||
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
||||||
.where(`${TableName.ProjectMembership}.userId`, userId)
|
|
||||||
.where(`${TableName.ProjectMembership}.projectId`, projectId)
|
|
||||||
.select(selectAllTableCols(TableName.ProjectUserMembershipRole))
|
|
||||||
.select(
|
.select(
|
||||||
|
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||||
|
// groups specific
|
||||||
|
db.ref("id").withSchema(TableName.GroupProjectMembership).as("groupMembershipId"),
|
||||||
|
db.ref("createdAt").withSchema(TableName.GroupProjectMembership).as("groupMembershipCreatedAt"),
|
||||||
|
db.ref("updatedAt").withSchema(TableName.GroupProjectMembership).as("groupMembershipUpdatedAt"),
|
||||||
|
db.ref("slug").withSchema("groupCustomRoles").as("userGroupProjectMembershipRoleCustomRoleSlug"),
|
||||||
|
db.ref("permissions").withSchema("groupCustomRoles").as("userGroupProjectMembershipRolePermission"),
|
||||||
|
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("userGroupProjectMembershipRoleId"),
|
||||||
|
db.ref("role").withSchema(TableName.GroupProjectMembershipRole).as("userGroupProjectMembershipRole"),
|
||||||
|
db
|
||||||
|
.ref("customRoleId")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleCustomRoleId"),
|
||||||
|
db
|
||||||
|
.ref("isTemporary")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleIsTemporary"),
|
||||||
|
db
|
||||||
|
.ref("temporaryMode")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleTemporaryMode"),
|
||||||
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleTemporaryRange"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessStartTime")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleTemporaryAccessStartTime"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessEndTime")
|
||||||
|
.withSchema(TableName.GroupProjectMembershipRole)
|
||||||
|
.as("userGroupProjectMembershipRoleTemporaryAccessEndTime"),
|
||||||
|
// user specific
|
||||||
db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"),
|
db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"),
|
||||||
db.ref("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"),
|
db.ref("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"),
|
||||||
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
|
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
|
||||||
db.ref("projectId").withSchema(TableName.ProjectMembership),
|
db.ref("slug").withSchema(TableName.ProjectRoles).as("userProjectMembershipRoleCustomRoleSlug"),
|
||||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
db.ref("permissions").withSchema(TableName.ProjectRoles).as("userProjectCustomRolePermission"),
|
||||||
db.ref("orgId").withSchema(TableName.Project),
|
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("userProjectMembershipRoleId"),
|
||||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
db.ref("role").withSchema(TableName.ProjectUserMembershipRole).as("userProjectMembershipRole"),
|
||||||
db.ref("permissions").withSchema(TableName.ProjectRoles),
|
db
|
||||||
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
|
.ref("temporaryMode")
|
||||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"),
|
.withSchema(TableName.ProjectUserMembershipRole)
|
||||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
|
.as("userProjectMembershipRoleTemporaryMode"),
|
||||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
|
db
|
||||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
|
.ref("isTemporary")
|
||||||
|
.withSchema(TableName.ProjectUserMembershipRole)
|
||||||
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"),
|
.as("userProjectMembershipRoleIsTemporary"),
|
||||||
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"),
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.ProjectUserMembershipRole)
|
||||||
|
.as("userProjectMembershipRoleTemporaryRange"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessStartTime")
|
||||||
|
.withSchema(TableName.ProjectUserMembershipRole)
|
||||||
|
.as("userProjectMembershipRoleTemporaryAccessStartTime"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessEndTime")
|
||||||
|
.withSchema(TableName.ProjectUserMembershipRole)
|
||||||
|
.as("userProjectMembershipRoleTemporaryAccessEndTime"),
|
||||||
|
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userAdditionalPrivilegesId"),
|
||||||
|
db
|
||||||
|
.ref("permissions")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("userAdditionalPrivilegesPermissions"),
|
||||||
|
db
|
||||||
|
.ref("temporaryMode")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("userAdditionalPrivilegesTemporaryMode"),
|
||||||
|
db
|
||||||
|
.ref("isTemporary")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("userAdditionalPrivilegesIsTemporary"),
|
||||||
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("userAdditionalPrivilegesTemporaryRange"),
|
||||||
|
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userAdditionalPrivilegesUserId"),
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessStartTime")
|
.ref("temporaryAccessStartTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
.as("userApTemporaryAccessStartTime"),
|
.as("userAdditionalPrivilegesTemporaryAccessStartTime"),
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessEndTime")
|
.ref("temporaryAccessEndTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
.as("userApTemporaryAccessEndTime")
|
.as("userAdditionalPrivilegesTemporaryAccessEndTime"),
|
||||||
|
// general
|
||||||
|
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||||
|
db.ref("orgId").withSchema(TableName.Project),
|
||||||
|
db.ref("id").withSchema(TableName.Project).as("projectId")
|
||||||
);
|
);
|
||||||
|
|
||||||
const permission = sqlNestRelationships({
|
const [userPermission] = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
key: "projectId",
|
key: "projectId",
|
||||||
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
|
parentMapper: ({
|
||||||
|
orgId,
|
||||||
|
orgAuthEnforced,
|
||||||
|
membershipId,
|
||||||
|
groupMembershipId,
|
||||||
|
membershipCreatedAt,
|
||||||
|
groupMembershipCreatedAt,
|
||||||
|
groupMembershipUpdatedAt,
|
||||||
|
membershipUpdatedAt
|
||||||
|
}) => ({
|
||||||
orgId,
|
orgId,
|
||||||
orgAuthEnforced,
|
orgAuthEnforced,
|
||||||
userId,
|
userId,
|
||||||
id: membershipId,
|
|
||||||
projectId,
|
projectId,
|
||||||
createdAt: membershipCreatedAt,
|
id: membershipId || groupMembershipId,
|
||||||
updatedAt: membershipUpdatedAt
|
createdAt: membershipCreatedAt || groupMembershipCreatedAt,
|
||||||
|
updatedAt: membershipUpdatedAt || groupMembershipUpdatedAt
|
||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "id",
|
key: "userGroupProjectMembershipRoleId",
|
||||||
label: "roles" as const,
|
label: "userGroupRoles" as const,
|
||||||
mapper: (data) =>
|
mapper: ({
|
||||||
ProjectUserMembershipRolesSchema.extend({
|
userGroupProjectMembershipRoleId,
|
||||||
permissions: z.unknown(),
|
userGroupProjectMembershipRole,
|
||||||
customRoleSlug: z.string().optional().nullable()
|
userGroupProjectMembershipRolePermission,
|
||||||
}).parse(data)
|
userGroupProjectMembershipRoleCustomRoleSlug,
|
||||||
|
userGroupProjectMembershipRoleIsTemporary,
|
||||||
|
userGroupProjectMembershipRoleTemporaryMode,
|
||||||
|
userGroupProjectMembershipRoleTemporaryAccessEndTime,
|
||||||
|
userGroupProjectMembershipRoleTemporaryAccessStartTime,
|
||||||
|
userGroupProjectMembershipRoleTemporaryRange
|
||||||
|
}) => ({
|
||||||
|
id: userGroupProjectMembershipRoleId,
|
||||||
|
role: userGroupProjectMembershipRole,
|
||||||
|
customRoleSlug: userGroupProjectMembershipRoleCustomRoleSlug,
|
||||||
|
permissions: userGroupProjectMembershipRolePermission,
|
||||||
|
temporaryRange: userGroupProjectMembershipRoleTemporaryRange,
|
||||||
|
temporaryMode: userGroupProjectMembershipRoleTemporaryMode,
|
||||||
|
temporaryAccessStartTime: userGroupProjectMembershipRoleTemporaryAccessStartTime,
|
||||||
|
temporaryAccessEndTime: userGroupProjectMembershipRoleTemporaryAccessEndTime,
|
||||||
|
isTemporary: userGroupProjectMembershipRoleIsTemporary
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "userApId",
|
key: "userProjectMembershipRoleId",
|
||||||
|
label: "projecMembershiptRoles" as const,
|
||||||
|
mapper: ({
|
||||||
|
userProjectMembershipRoleId,
|
||||||
|
userProjectMembershipRole,
|
||||||
|
userProjectCustomRolePermission,
|
||||||
|
userProjectMembershipRoleIsTemporary,
|
||||||
|
userProjectMembershipRoleTemporaryMode,
|
||||||
|
userProjectMembershipRoleTemporaryRange,
|
||||||
|
userProjectMembershipRoleTemporaryAccessEndTime,
|
||||||
|
userProjectMembershipRoleTemporaryAccessStartTime,
|
||||||
|
userProjectMembershipRoleCustomRoleSlug
|
||||||
|
}) => ({
|
||||||
|
id: userProjectMembershipRoleId,
|
||||||
|
role: userProjectMembershipRole,
|
||||||
|
customRoleSlug: userProjectMembershipRoleCustomRoleSlug,
|
||||||
|
permissions: userProjectCustomRolePermission,
|
||||||
|
temporaryRange: userProjectMembershipRoleTemporaryRange,
|
||||||
|
temporaryMode: userProjectMembershipRoleTemporaryMode,
|
||||||
|
temporaryAccessStartTime: userProjectMembershipRoleTemporaryAccessStartTime,
|
||||||
|
temporaryAccessEndTime: userProjectMembershipRoleTemporaryAccessEndTime,
|
||||||
|
isTemporary: userProjectMembershipRoleIsTemporary
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "userAdditionalPrivilegesId",
|
||||||
label: "additionalPrivileges" as const,
|
label: "additionalPrivileges" as const,
|
||||||
mapper: ({
|
mapper: ({
|
||||||
userApId,
|
userAdditionalPrivilegesId,
|
||||||
userApPermissions,
|
userAdditionalPrivilegesPermissions,
|
||||||
userApIsTemporary,
|
userAdditionalPrivilegesIsTemporary,
|
||||||
userApTemporaryMode,
|
userAdditionalPrivilegesTemporaryMode,
|
||||||
userApTemporaryRange,
|
userAdditionalPrivilegesTemporaryRange,
|
||||||
userApTemporaryAccessEndTime,
|
userAdditionalPrivilegesTemporaryAccessEndTime,
|
||||||
userApTemporaryAccessStartTime
|
userAdditionalPrivilegesTemporaryAccessStartTime
|
||||||
}) => ({
|
}) => ({
|
||||||
id: userApId,
|
id: userAdditionalPrivilegesId,
|
||||||
permissions: userApPermissions,
|
permissions: userAdditionalPrivilegesPermissions,
|
||||||
temporaryRange: userApTemporaryRange,
|
temporaryRange: userAdditionalPrivilegesTemporaryRange,
|
||||||
temporaryMode: userApTemporaryMode,
|
temporaryMode: userAdditionalPrivilegesTemporaryMode,
|
||||||
temporaryAccessEndTime: userApTemporaryAccessEndTime,
|
temporaryAccessStartTime: userAdditionalPrivilegesTemporaryAccessStartTime,
|
||||||
temporaryAccessStartTime: userApTemporaryAccessStartTime,
|
temporaryAccessEndTime: userAdditionalPrivilegesTemporaryAccessEndTime,
|
||||||
isTemporary: userApIsTemporary
|
isTemporary: userAdditionalPrivilegesIsTemporary
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPermission = groupDocs.length
|
if (!userPermission) return undefined;
|
||||||
? sqlNestRelationships({
|
if (!userPermission?.userGroupRoles?.[0] && !userPermission?.projecMembershiptRoles?.[0]) return undefined;
|
||||||
data: groupDocs,
|
|
||||||
key: "projectId",
|
|
||||||
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
|
|
||||||
orgId,
|
|
||||||
orgAuthEnforced,
|
|
||||||
userId,
|
|
||||||
id: membershipId,
|
|
||||||
projectId,
|
|
||||||
createdAt: membershipCreatedAt,
|
|
||||||
updatedAt: membershipUpdatedAt
|
|
||||||
}),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "id",
|
|
||||||
label: "roles" as const,
|
|
||||||
mapper: (data) =>
|
|
||||||
ProjectUserMembershipRolesSchema.extend({
|
|
||||||
permissions: z.unknown(),
|
|
||||||
customRoleSlug: z.string().optional().nullable()
|
|
||||||
}).parse(data)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "userApId",
|
|
||||||
label: "additionalPrivileges" as const,
|
|
||||||
mapper: ({
|
|
||||||
userApId,
|
|
||||||
userApProjectId,
|
|
||||||
userApUserId,
|
|
||||||
userApPermissions,
|
|
||||||
userApIsTemporary,
|
|
||||||
userApTemporaryMode,
|
|
||||||
userApTemporaryRange,
|
|
||||||
userApTemporaryAccessEndTime,
|
|
||||||
userApTemporaryAccessStartTime
|
|
||||||
}) => ({
|
|
||||||
id: userApId,
|
|
||||||
userId: userApUserId,
|
|
||||||
projectId: userApProjectId,
|
|
||||||
permissions: userApPermissions,
|
|
||||||
temporaryRange: userApTemporaryRange,
|
|
||||||
temporaryMode: userApTemporaryMode,
|
|
||||||
temporaryAccessEndTime: userApTemporaryAccessEndTime,
|
|
||||||
temporaryAccessStartTime: userApTemporaryAccessStartTime,
|
|
||||||
isTemporary: userApIsTemporary
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
if (!permission?.[0] && !groupPermission[0]) return undefined;
|
|
||||||
|
|
||||||
// when introducting cron mode change it here
|
// when introducting cron mode change it here
|
||||||
const activeRoles =
|
const activeRoles =
|
||||||
permission?.[0]?.roles?.filter(
|
userPermission?.projecMembershiptRoles?.filter(
|
||||||
({ isTemporary, temporaryAccessEndTime }) =>
|
({ isTemporary, temporaryAccessEndTime }) =>
|
||||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
const activeGroupRoles =
|
const activeGroupRoles =
|
||||||
groupPermission?.[0]?.roles?.filter(
|
userPermission?.userGroupRoles?.filter(
|
||||||
({ isTemporary, temporaryAccessEndTime }) =>
|
({ isTemporary, temporaryAccessEndTime }) =>
|
||||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
const activeAdditionalPrivileges =
|
const activeAdditionalPrivileges =
|
||||||
permission?.[0]?.additionalPrivileges?.filter(
|
userPermission?.additionalPrivileges?.filter(
|
||||||
({ isTemporary, temporaryAccessEndTime }) =>
|
({ isTemporary, temporaryAccessEndTime }) =>
|
||||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
const activeGroupAdditionalPrivileges =
|
|
||||||
groupPermission?.[0]?.additionalPrivileges?.filter(
|
|
||||||
({ isTemporary, temporaryAccessEndTime, userId: apUserId, projectId: apProjectId }) =>
|
|
||||||
apProjectId === projectId &&
|
|
||||||
apUserId === userId &&
|
|
||||||
(!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime))
|
|
||||||
) ?? [];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(permission[0] || groupPermission[0]),
|
...userPermission,
|
||||||
roles: [...activeRoles, ...activeGroupRoles],
|
roles: [...activeRoles, ...activeGroupRoles],
|
||||||
additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges]
|
additionalPrivileges: activeAdditionalPrivileges
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||||
|
@ -20,7 +20,7 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok
|
|||||||
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
||||||
import { TPermissionDALFactory } from "./permission-dal";
|
import { TPermissionDALFactory } from "./permission-dal";
|
||||||
import { validateOrgSAML } from "./permission-fns";
|
import { validateOrgSAML } from "./permission-fns";
|
||||||
import { TBuildProjectPermissionDTO } from "./permission-types";
|
import { TBuildOrgPermissionDTO, TBuildProjectPermissionDTO } from "./permission-types";
|
||||||
import {
|
import {
|
||||||
buildServiceTokenProjectPermission,
|
buildServiceTokenProjectPermission,
|
||||||
projectAdminPermissions,
|
projectAdminPermissions,
|
||||||
@ -47,26 +47,29 @@ export const permissionServiceFactory = ({
|
|||||||
serviceTokenDAL,
|
serviceTokenDAL,
|
||||||
projectDAL
|
projectDAL
|
||||||
}: TPermissionServiceFactoryDep) => {
|
}: TPermissionServiceFactoryDep) => {
|
||||||
const buildOrgPermission = (role: string, permission?: unknown) => {
|
const buildOrgPermission = (orgUserRoles: TBuildOrgPermissionDTO) => {
|
||||||
switch (role) {
|
const rules = orgUserRoles
|
||||||
case OrgMembershipRole.Admin:
|
.map(({ role, permissions }) => {
|
||||||
return orgAdminPermissions;
|
switch (role) {
|
||||||
case OrgMembershipRole.Member:
|
case OrgMembershipRole.Admin:
|
||||||
return orgMemberPermissions;
|
return orgAdminPermissions;
|
||||||
case OrgMembershipRole.NoAccess:
|
case OrgMembershipRole.Member:
|
||||||
return orgNoAccessPermissions;
|
return orgMemberPermissions;
|
||||||
case OrgMembershipRole.Custom:
|
case OrgMembershipRole.NoAccess:
|
||||||
return createMongoAbility<OrgPermissionSet>(
|
return orgNoAccessPermissions;
|
||||||
unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(
|
case OrgMembershipRole.Custom:
|
||||||
permission as PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]
|
return unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(
|
||||||
),
|
permissions as PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]
|
||||||
{
|
);
|
||||||
conditionsMatcher
|
default:
|
||||||
}
|
throw new BadRequestError({ name: "OrgRoleInvalid", message: "Org role not found" });
|
||||||
);
|
}
|
||||||
default:
|
})
|
||||||
throw new BadRequestError({ name: "OrgRoleInvalid", message: "Org role not found" });
|
.reduce((curr, prev) => prev.concat(curr), []);
|
||||||
}
|
|
||||||
|
return createMongoAbility<OrgPermissionSet>(rules, {
|
||||||
|
conditionsMatcher
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildProjectPermission = (projectUserRoles: TBuildProjectPermissionDTO) => {
|
const buildProjectPermission = (projectUserRoles: TBuildProjectPermissionDTO) => {
|
||||||
@ -129,7 +132,13 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
validateOrgSAML(authMethod, membership.orgAuthEnforced);
|
validateOrgSAML(authMethod, membership.orgAuthEnforced);
|
||||||
|
|
||||||
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
const finalPolicyRoles = [{ role: membership.role, permissions: membership.permissions }].concat(
|
||||||
|
membership?.groups?.map(({ role, customRolePermission }) => ({
|
||||||
|
role,
|
||||||
|
permissions: customRolePermission
|
||||||
|
})) || []
|
||||||
|
);
|
||||||
|
return { permission: buildOrgPermission(finalPolicyRoles), membership };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIdentityOrgPermission = async (identityId: string, orgId: string) => {
|
const getIdentityOrgPermission = async (identityId: string, orgId: string) => {
|
||||||
@ -138,7 +147,10 @@ export const permissionServiceFactory = ({
|
|||||||
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
|
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
|
||||||
throw new BadRequestError({ name: "Custom permission not found" });
|
throw new BadRequestError({ name: "Custom permission not found" });
|
||||||
}
|
}
|
||||||
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
return {
|
||||||
|
permission: buildOrgPermission([{ role: membership.role, permissions: membership.permissions }]),
|
||||||
|
membership
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrgPermission = async (
|
const getOrgPermission = async (
|
||||||
@ -169,11 +181,11 @@ export const permissionServiceFactory = ({
|
|||||||
const orgRole = await orgRoleDAL.findOne({ slug: role, orgId });
|
const orgRole = await orgRoleDAL.findOne({ slug: role, orgId });
|
||||||
if (!orgRole) throw new BadRequestError({ message: "Role not found" });
|
if (!orgRole) throw new BadRequestError({ message: "Role not found" });
|
||||||
return {
|
return {
|
||||||
permission: buildOrgPermission(OrgMembershipRole.Custom, orgRole.permissions),
|
permission: buildOrgPermission([{ role: OrgMembershipRole.Custom, permissions: orgRole.permissions }]),
|
||||||
role: orgRole
|
role: orgRole
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { permission: buildOrgPermission(role, []) };
|
return { permission: buildOrgPermission([{ role, permissions: [] }]) };
|
||||||
};
|
};
|
||||||
|
|
||||||
// user permission for a project in an organization
|
// user permission for a project in an organization
|
||||||
|
@ -2,3 +2,8 @@ export type TBuildProjectPermissionDTO = {
|
|||||||
permissions?: unknown;
|
permissions?: unknown;
|
||||||
role: string;
|
role: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
export type TBuildOrgPermissionDTO = {
|
||||||
|
permissions?: unknown;
|
||||||
|
role: string;
|
||||||
|
}[];
|
||||||
|
@ -91,6 +91,8 @@ export type TQueueJobTypes = {
|
|||||||
[QueueName.IntegrationSync]: {
|
[QueueName.IntegrationSync]: {
|
||||||
name: QueueJobs.IntegrationSync;
|
name: QueueJobs.IntegrationSync;
|
||||||
payload: {
|
payload: {
|
||||||
|
isManual?: boolean;
|
||||||
|
actorId?: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import fastifyPlugin from "fastify-plugin";
|
import fastifyPlugin from "fastify-plugin";
|
||||||
import { JsonWebTokenError } from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -44,7 +44,7 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider
|
|||||||
detail: error.detail
|
detail: error.detail
|
||||||
});
|
});
|
||||||
// Handle JWT errors and make them more human-readable for the end-user.
|
// Handle JWT errors and make them more human-readable for the end-user.
|
||||||
} else if (error instanceof JsonWebTokenError) {
|
} else if (error instanceof jwt.JsonWebTokenError) {
|
||||||
const message = (() => {
|
const message = (() => {
|
||||||
if (error.message === JWTErrors.JwtExpired) {
|
if (error.message === JWTErrors.JwtExpired) {
|
||||||
return "Your token has expired. Please re-authenticate.";
|
return "Your token has expired. Please re-authenticate.";
|
||||||
|
@ -810,6 +810,8 @@ export const registerRoutes = async (
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
webhookDAL,
|
webhookDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
|
auditLogService,
|
||||||
|
userDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
smtpService,
|
smtpService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
|
@ -4,7 +4,7 @@ import { IntegrationsSchema } from "@app/db/schemas";
|
|||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { INTEGRATION } from "@app/lib/api-docs";
|
import { INTEGRATION } from "@app/lib/api-docs";
|
||||||
import { removeTrailingSlash, shake } from "@app/lib/fn";
|
import { removeTrailingSlash, shake } from "@app/lib/fn";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -154,6 +154,48 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:integrationId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Get an integration by integration id",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
integrationId: z.string().trim().describe(INTEGRATION.UPDATE.integrationId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
integration: IntegrationsSchema.extend({
|
||||||
|
environment: z.object({
|
||||||
|
slug: z.string().trim(),
|
||||||
|
name: z.string().trim(),
|
||||||
|
id: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const integration = await server.services.integration.getIntegration({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
id: req.params.integrationId
|
||||||
|
});
|
||||||
|
|
||||||
|
return { integration };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:integrationId",
|
url: "/:integrationId",
|
||||||
|
@ -14,7 +14,7 @@ import { AUDIT_LOGS, ORGANIZATIONS } from "@app/lib/api-docs";
|
|||||||
import { getLastMidnightDateISO } from "@app/lib/fn";
|
import { getLastMidnightDateISO } from "@app/lib/fn";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@ -74,8 +74,35 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
schema: {
|
schema: {
|
||||||
description: "Get all audit logs for an organization",
|
description: "Get all audit logs for an organization",
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType),
|
projectId: z.string().optional(),
|
||||||
|
actorType: z.nativeEnum(ActorType).optional(),
|
||||||
|
// eventType is split with , for multiple values, we need to transform it to array
|
||||||
|
eventType: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => (val ? val.split(",") : undefined)),
|
||||||
userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType),
|
userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType),
|
||||||
|
eventMetadata: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
if (!val) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairs = val.split(",");
|
||||||
|
|
||||||
|
return pairs.reduce(
|
||||||
|
(acc, pair) => {
|
||||||
|
const [key, value] = pair.split("=");
|
||||||
|
if (key && value) {
|
||||||
|
acc[key] = value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
}),
|
||||||
startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate),
|
startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate),
|
||||||
endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate),
|
endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate),
|
||||||
offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset),
|
offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset),
|
||||||
@ -114,13 +141,19 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const auditLogs = await server.services.auditLog.listAuditLogs({
|
const auditLogs = await server.services.auditLog.listAuditLogs({
|
||||||
|
filter: {
|
||||||
|
...req.query,
|
||||||
|
endDate: req.query.endDate,
|
||||||
|
projectId: req.query.projectId,
|
||||||
|
startDate: req.query.startDate || getLastMidnightDateISO(),
|
||||||
|
auditLogActorId: req.query.actor,
|
||||||
|
actorType: req.query.actorType,
|
||||||
|
eventType: req.query.eventType as EventType[] | undefined
|
||||||
|
},
|
||||||
|
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
...req.query,
|
|
||||||
endDate: req.query.endDate,
|
|
||||||
startDate: req.query.startDate || getLastMidnightDateISO(),
|
|
||||||
auditLogActor: req.query.actor,
|
|
||||||
actor: req.permission.type
|
actor: req.permission.type
|
||||||
});
|
});
|
||||||
return { auditLogs };
|
return { auditLogs };
|
||||||
|
@ -34,6 +34,7 @@ export enum AuthMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ActorType { // would extend to AWS, Azure, ...
|
export enum ActorType { // would extend to AWS, Azure, ...
|
||||||
|
PLATFORM = "platform", // Useful for when we want to perform logging on automated actions such as integration syncs.
|
||||||
USER = "user", // userIdentity
|
USER = "user", // userIdentity
|
||||||
SERVICE = "service",
|
SERVICE = "service",
|
||||||
IDENTITY = "identity",
|
IDENTITY = "identity",
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
infisicalSymmetricDecrypt,
|
infisicalSymmetricDecrypt,
|
||||||
infisicalSymmetricEncypt
|
infisicalSymmetricEncypt
|
||||||
} from "@app/lib/crypto/encryption";
|
} from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
@ -68,12 +68,12 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
identityId: identityOidcAuth.identityId
|
identityId: identityOidcAuth.identityId
|
||||||
});
|
});
|
||||||
if (!identityMembershipOrg) {
|
if (!identityMembershipOrg) {
|
||||||
throw new BadRequestError({ message: "Failed to find identity" });
|
throw new NotFoundError({ message: "Failed to find identity in organization" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId });
|
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId });
|
||||||
if (!orgBot) {
|
if (!orgBot) {
|
||||||
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
throw new NotFoundError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = infisicalSymmetricDecrypt({
|
const key = infisicalSymmetricDecrypt({
|
||||||
@ -106,7 +106,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
|
|
||||||
const decodedToken = jwt.decode(oidcJwt, { complete: true });
|
const decodedToken = jwt.decode(oidcJwt, { complete: true });
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw new BadRequestError({
|
throw new UnauthorizedError({
|
||||||
message: "Invalid JWT"
|
message: "Invalid JWT"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -119,13 +119,24 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
const { kid } = decodedToken.header;
|
const { kid } = decodedToken.header;
|
||||||
const oidcSigningKey = await client.getSigningKey(kid);
|
const oidcSigningKey = await client.getSigningKey(kid);
|
||||||
|
|
||||||
const tokenData = jwt.verify(oidcJwt, oidcSigningKey.getPublicKey(), {
|
let tokenData: Record<string, string>;
|
||||||
issuer: identityOidcAuth.boundIssuer
|
try {
|
||||||
}) as Record<string, string>;
|
tokenData = jwt.verify(oidcJwt, oidcSigningKey.getPublicKey(), {
|
||||||
|
issuer: identityOidcAuth.boundIssuer
|
||||||
|
}) as Record<string, string>;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof jwt.JsonWebTokenError) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: `Access denied: ${error.message}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
if (identityOidcAuth.boundSubject) {
|
if (identityOidcAuth.boundSubject) {
|
||||||
if (!doesFieldValueMatchOidcPolicy(tokenData.sub, identityOidcAuth.boundSubject)) {
|
if (!doesFieldValueMatchOidcPolicy(tokenData.sub, identityOidcAuth.boundSubject)) {
|
||||||
throw new ForbiddenRequestError({
|
throw new UnauthorizedError({
|
||||||
message: "Access denied: OIDC subject not allowed."
|
message: "Access denied: OIDC subject not allowed."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -137,7 +148,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
.split(", ")
|
.split(", ")
|
||||||
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
|
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
|
||||||
) {
|
) {
|
||||||
throw new ForbiddenRequestError({
|
throw new UnauthorizedError({
|
||||||
message: "Access denied: OIDC audience not allowed."
|
message: "Access denied: OIDC audience not allowed."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,7 +161,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (
|
if (
|
||||||
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
|
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
|
||||||
) {
|
) {
|
||||||
throw new ForbiddenRequestError({
|
throw new UnauthorizedError({
|
||||||
message: "Access denied: OIDC claim not allowed."
|
message: "Access denied: OIDC claim not allowed."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
|||||||
|
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth-dal";
|
import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth-dal";
|
||||||
@ -19,6 +19,7 @@ import { TIntegrationDALFactory } from "./integration-dal";
|
|||||||
import {
|
import {
|
||||||
TCreateIntegrationDTO,
|
TCreateIntegrationDTO,
|
||||||
TDeleteIntegrationDTO,
|
TDeleteIntegrationDTO,
|
||||||
|
TGetIntegrationDTO,
|
||||||
TSyncIntegrationDTO,
|
TSyncIntegrationDTO,
|
||||||
TUpdateIntegrationDTO
|
TUpdateIntegrationDTO
|
||||||
} from "./integration-types";
|
} from "./integration-types";
|
||||||
@ -180,6 +181,27 @@ export const integrationServiceFactory = ({
|
|||||||
return updatedIntegration;
|
return updatedIntegration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getIntegration = async ({ id, actor, actorAuthMethod, actorId, actorOrgId }: TGetIntegrationDTO) => {
|
||||||
|
const integration = await integrationDAL.findById(id);
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
integration?.projectId || "",
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
|
if (!integration) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: "Integration not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...integration, envId: integration.environment.id };
|
||||||
|
};
|
||||||
|
|
||||||
const deleteIntegration = async ({
|
const deleteIntegration = async ({
|
||||||
actorId,
|
actorId,
|
||||||
id,
|
id,
|
||||||
@ -276,6 +298,8 @@ export const integrationServiceFactory = ({
|
|||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
await secretQueueService.syncIntegrations({
|
await secretQueueService.syncIntegrations({
|
||||||
|
isManual: true,
|
||||||
|
actorId,
|
||||||
environment: integration.environment.slug,
|
environment: integration.environment.slug,
|
||||||
secretPath: integration.secretPath,
|
secretPath: integration.secretPath,
|
||||||
projectId: integration.projectId
|
projectId: integration.projectId
|
||||||
@ -289,6 +313,7 @@ export const integrationServiceFactory = ({
|
|||||||
updateIntegration,
|
updateIntegration,
|
||||||
deleteIntegration,
|
deleteIntegration,
|
||||||
listIntegrationByProject,
|
listIntegrationByProject,
|
||||||
|
getIntegration,
|
||||||
syncIntegration
|
syncIntegration
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -39,6 +39,10 @@ export type TCreateIntegrationDTO = {
|
|||||||
};
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetIntegrationDTO = {
|
||||||
|
id: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateIntegrationDTO = {
|
export type TUpdateIntegrationDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
app?: string;
|
app?: string;
|
||||||
|
@ -60,7 +60,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "Admin",
|
name: "Admin",
|
||||||
slug: "admin",
|
slug: "admin",
|
||||||
description: "Complete administration access over the organization",
|
description: "Complete administration access over the organization",
|
||||||
permissions: packRules(orgAdminPermissions.rules),
|
permissions: packRules(orgAdminPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
};
|
};
|
||||||
@ -72,7 +72,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "Member",
|
name: "Member",
|
||||||
slug: "member",
|
slug: "member",
|
||||||
description: "Non-administrative role in an organization",
|
description: "Non-administrative role in an organization",
|
||||||
permissions: packRules(orgMemberPermissions.rules),
|
permissions: packRules(orgMemberPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
};
|
};
|
||||||
@ -84,7 +84,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "No Access",
|
name: "No Access",
|
||||||
slug: "no-access",
|
slug: "no-access",
|
||||||
description: "No access to any resources in the organization",
|
description: "No access to any resources in the organization",
|
||||||
permissions: packRules(orgNoAccessPermissions.rules),
|
permissions: packRules(orgNoAccessPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
};
|
};
|
||||||
@ -151,7 +151,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "Admin",
|
name: "Admin",
|
||||||
slug: "admin",
|
slug: "admin",
|
||||||
description: "Complete administration access over the organization",
|
description: "Complete administration access over the organization",
|
||||||
permissions: packRules(orgAdminPermissions.rules),
|
permissions: packRules(orgAdminPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
@ -161,7 +161,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "Member",
|
name: "Member",
|
||||||
slug: "member",
|
slug: "member",
|
||||||
description: "Non-administrative role in an organization",
|
description: "Non-administrative role in an organization",
|
||||||
permissions: packRules(orgMemberPermissions.rules),
|
permissions: packRules(orgMemberPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
@ -171,7 +171,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol
|
|||||||
name: "No Access",
|
name: "No Access",
|
||||||
slug: "no-access",
|
slug: "no-access",
|
||||||
description: "No access to any resources in the organization",
|
description: "No access to any resources in the organization",
|
||||||
permissions: packRules(orgNoAccessPermissions.rules),
|
permissions: packRules(orgNoAccessPermissions),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
},
|
},
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
import { ProjectUpgradeStatus, ProjectVersion, TSecretSnapshotSecretsV2, TSecretVersionsV2 } from "@app/db/schemas";
|
import { ProjectUpgradeStatus, ProjectVersion, TSecretSnapshotSecretsV2, TSecretVersionsV2 } from "@app/db/schemas";
|
||||||
|
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||||
|
import { Actor, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
||||||
import { TSecretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal";
|
import { TSecretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal";
|
||||||
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
||||||
@ -21,6 +23,7 @@ import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version
|
|||||||
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
|
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
|
||||||
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||||
|
|
||||||
|
import { ActorType } from "../auth/auth-type";
|
||||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||||
import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth-dal";
|
import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth-dal";
|
||||||
import { TIntegrationAuthServiceFactory } from "../integration-auth/integration-auth-service";
|
import { TIntegrationAuthServiceFactory } from "../integration-auth/integration-auth-service";
|
||||||
@ -40,6 +43,7 @@ import { expandSecretReferencesFactory, getAllNestedSecretReferences } from "../
|
|||||||
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TWebhookDALFactory } from "../webhook/webhook-dal";
|
import { TWebhookDALFactory } from "../webhook/webhook-dal";
|
||||||
import { fnTriggerWebhook } from "../webhook/webhook-fns";
|
import { fnTriggerWebhook } from "../webhook/webhook-fns";
|
||||||
import { TSecretDALFactory } from "./secret-dal";
|
import { TSecretDALFactory } from "./secret-dal";
|
||||||
@ -71,6 +75,7 @@ type TSecretQueueFactoryDep = {
|
|||||||
secretVersionDAL: TSecretVersionDALFactory;
|
secretVersionDAL: TSecretVersionDALFactory;
|
||||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||||
secretTagDAL: TSecretTagDALFactory;
|
secretTagDAL: TSecretTagDALFactory;
|
||||||
|
userDAL: Pick<TUserDALFactory, "findById">;
|
||||||
secretVersionTagDAL: TSecretVersionTagDALFactory;
|
secretVersionTagDAL: TSecretVersionTagDALFactory;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
secretV2BridgeDAL: TSecretV2BridgeDALFactory;
|
secretV2BridgeDAL: TSecretV2BridgeDALFactory;
|
||||||
@ -81,6 +86,7 @@ type TSecretQueueFactoryDep = {
|
|||||||
snapshotDAL: Pick<TSnapshotDALFactory, "findNSecretV1SnapshotByFolderId" | "deleteSnapshotsAboveLimit">;
|
snapshotDAL: Pick<TSnapshotDALFactory, "findNSecretV1SnapshotByFolderId" | "deleteSnapshotsAboveLimit">;
|
||||||
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany" | "batchInsert">;
|
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany" | "batchInsert">;
|
||||||
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
|
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
|
||||||
|
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecrets = {
|
export type TGetSecrets = {
|
||||||
@ -106,6 +112,7 @@ export const secretQueueFactory = ({
|
|||||||
secretDAL,
|
secretDAL,
|
||||||
secretImportDAL,
|
secretImportDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
userDAL,
|
||||||
webhookDAL,
|
webhookDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
@ -125,7 +132,8 @@ export const secretQueueFactory = ({
|
|||||||
snapshotDAL,
|
snapshotDAL,
|
||||||
snapshotSecretV2BridgeDAL,
|
snapshotSecretV2BridgeDAL,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
auditLogService
|
||||||
}: TSecretQueueFactoryDep) => {
|
}: TSecretQueueFactoryDep) => {
|
||||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@ -430,7 +438,9 @@ export const secretQueueFactory = ({
|
|||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncIntegrations = async (dto: TGetSecrets & { deDupeQueue?: Record<string, boolean> }) => {
|
const syncIntegrations = async (
|
||||||
|
dto: TGetSecrets & { isManual?: boolean; actorId?: string; deDupeQueue?: Record<string, boolean> }
|
||||||
|
) => {
|
||||||
await queueService.queue(QueueName.IntegrationSync, QueueJobs.IntegrationSync, dto, {
|
await queueService.queue(QueueName.IntegrationSync, QueueJobs.IntegrationSync, dto, {
|
||||||
attempts: 3,
|
attempts: 3,
|
||||||
delay: 1000,
|
delay: 1000,
|
||||||
@ -528,7 +538,7 @@ export const secretQueueFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await syncIntegrations({ secretPath, projectId, environment, deDupeQueue });
|
await syncIntegrations({ secretPath, projectId, environment, deDupeQueue, isManual: false });
|
||||||
if (!excludeReplication) {
|
if (!excludeReplication) {
|
||||||
await replicateSecrets({
|
await replicateSecrets({
|
||||||
_deDupeReplicationQueue: deDupeReplicationQueue,
|
_deDupeReplicationQueue: deDupeReplicationQueue,
|
||||||
@ -544,7 +554,7 @@ export const secretQueueFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
queueService.start(QueueName.IntegrationSync, async (job) => {
|
queueService.start(QueueName.IntegrationSync, async (job) => {
|
||||||
const { environment, projectId, secretPath, depth = 1, deDupeQueue = {} } = job.data;
|
const { environment, actorId, isManual, projectId, secretPath, depth = 1, deDupeQueue = {} } = job.data;
|
||||||
if (depth > MAX_SYNC_SECRET_DEPTH) return;
|
if (depth > MAX_SYNC_SECRET_DEPTH) return;
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
@ -693,6 +703,30 @@ export const secretQueueFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generateActor = async (): Promise<Actor> => {
|
||||||
|
if (isManual && actorId) {
|
||||||
|
const user = await userDAL.findById(actorId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: ActorType.USER,
|
||||||
|
metadata: {
|
||||||
|
email: user.email,
|
||||||
|
username: user.username,
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: ActorType.PLATFORM,
|
||||||
|
metadata: {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// akhilmhdh: this try catch is for lock release
|
// akhilmhdh: this try catch is for lock release
|
||||||
try {
|
try {
|
||||||
const secrets = shouldUseSecretV2Bridge
|
const secrets = shouldUseSecretV2Bridge
|
||||||
@ -778,6 +812,21 @@ export const secretQueueFactory = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId,
|
||||||
|
actor: await generateActor(),
|
||||||
|
event: {
|
||||||
|
type: EventType.INTEGRATION_SYNCED,
|
||||||
|
metadata: {
|
||||||
|
integrationId: integration.id,
|
||||||
|
isSynced: response?.isSynced ?? true,
|
||||||
|
lastSyncJobId: job?.id ?? "",
|
||||||
|
lastUsed: new Date(),
|
||||||
|
syncMessage: response?.syncMessage ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await integrationDAL.updateById(integration.id, {
|
await integrationDAL.updateById(integration.id, {
|
||||||
lastSyncJobId: job.id,
|
lastSyncJobId: job.id,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
@ -794,9 +843,23 @@ export const secretQueueFactory = ({
|
|||||||
(err instanceof AxiosError ? JSON.stringify(err?.response?.data) : (err as Error)?.message) ||
|
(err instanceof AxiosError ? JSON.stringify(err?.response?.data) : (err as Error)?.message) ||
|
||||||
"Unknown error occurred.";
|
"Unknown error occurred.";
|
||||||
|
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId,
|
||||||
|
actor: await generateActor(),
|
||||||
|
event: {
|
||||||
|
type: EventType.INTEGRATION_SYNCED,
|
||||||
|
metadata: {
|
||||||
|
integrationId: integration.id,
|
||||||
|
isSynced: false,
|
||||||
|
lastSyncJobId: job?.id ?? "",
|
||||||
|
lastUsed: new Date(),
|
||||||
|
syncMessage: message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await integrationDAL.updateById(integration.id, {
|
await integrationDAL.updateById(integration.id, {
|
||||||
lastSyncJobId: job.id,
|
lastSyncJobId: job.id,
|
||||||
lastUsed: new Date(),
|
|
||||||
syncMessage: message,
|
syncMessage: message,
|
||||||
isSynced: false
|
isSynced: false
|
||||||
});
|
});
|
||||||
|
@ -5,9 +5,11 @@ description: "Learn how to setup Slack integration"
|
|||||||
|
|
||||||
This guide will provide step by step instructions on how to configure Slack integration for your Infisical projects.
|
This guide will provide step by step instructions on how to configure Slack integration for your Infisical projects.
|
||||||
|
|
||||||
|
## Setting up Slack integration in your projects
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Infisical Cloud">
|
<Tab title="Infisical Cloud">
|
||||||
## Create Slack workflow integration
|
### Create Slack workflow integration
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Navigate to the Workflow Integrations tab in your organization settings">
|
<Step title="Navigate to the Workflow Integrations tab in your organization settings">
|
||||||
In order to use Slack integration in your projects, you will first have to
|
In order to use Slack integration in your projects, you will first have to
|
||||||
@ -32,7 +34,7 @@ This guide will provide step by step instructions on how to configure Slack inte
|
|||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Configure project to use Slack workflow integration
|
### Configure project to use Slack workflow integration
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Navigate to the Workflow Integrations tab in the project settings">
|
<Step title="Navigate to the Workflow Integrations tab in the project settings">
|
||||||
@ -56,7 +58,7 @@ This guide will provide step by step instructions on how to configure Slack inte
|
|||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Self-hosted setup">
|
<Tab title="Self-hosted setup">
|
||||||
## Configure admin settings
|
### Configure admin settings
|
||||||
Note that this step only has to be done once for the entire instance.
|
Note that this step only has to be done once for the entire instance.
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
@ -90,7 +92,7 @@ This guide will provide step by step instructions on how to configure Slack inte
|
|||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Create Slack workflow integration
|
### Create Slack workflow integration
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Navigate to the Workflow Integrations tab in your organization settings">
|
<Step title="Navigate to the Workflow Integrations tab in your organization settings">
|
||||||
@ -116,7 +118,7 @@ This guide will provide step by step instructions on how to configure Slack inte
|
|||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Configure project to use Slack workflow integration
|
### Configure project to use Slack workflow integration
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Navigate to the Workflow Integrations tab in the project settings">
|
<Step title="Navigate to the Workflow Integrations tab in the project settings">
|
||||||
@ -140,3 +142,23 @@ This guide will provide step by step instructions on how to configure Slack inte
|
|||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
## Using the Slack integration in your private channels
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="In the Apps section on Slack, find the Infisical app and view the app details">
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
<Step title="Select Add this app to a channel">
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
<Step title="Find the private channel you want to setup notifications for and press Add">
|
||||||
|

|
||||||
|
You can now view the private channels in the Slack channel selection fields!
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 670 KiB |
Binary file not shown.
After Width: | Height: | Size: 736 KiB |
Binary file not shown.
After Width: | Height: | Size: 506 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
30
frontend/package-lock.json
generated
30
frontend/package-lock.json
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "relock-npm-lock-v2-SvMQeF",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"i18next-http-backend": "^2.2.0",
|
"i18next-http-backend": "^2.2.0",
|
||||||
"infisical-node": "^1.0.37",
|
"infisical-node": "^1.0.37",
|
||||||
"ip": "^2.0.1",
|
"ip": "^2.0.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.2",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"lottie-react": "^2.4.0",
|
"lottie-react": "^2.4.0",
|
||||||
@ -12729,9 +12729,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "2.4.7",
|
"version": "2.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz",
|
||||||
"integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==",
|
"integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
@ -16873,22 +16874,29 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
"node_modules/jspdf": {
|
"node_modules/jspdf": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz",
|
||||||
"integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
|
"integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.14.0",
|
"@babel/runtime": "^7.23.2",
|
||||||
"atob": "^2.1.2",
|
"atob": "^2.1.2",
|
||||||
"btoa": "^1.2.1",
|
"btoa": "^1.2.1",
|
||||||
"fflate": "^0.4.8"
|
"fflate": "^0.8.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"canvg": "^3.0.6",
|
"canvg": "^3.0.6",
|
||||||
"core-js": "^3.6.0",
|
"core-js": "^3.6.0",
|
||||||
"dompurify": "^2.2.0",
|
"dompurify": "^2.5.4",
|
||||||
"html2canvas": "^1.0.0-rc.5"
|
"html2canvas": "^1.0.0-rc.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jspdf/node_modules/fflate": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jsprim": {
|
"node_modules/jsprim": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
"i18next-http-backend": "^2.2.0",
|
"i18next-http-backend": "^2.2.0",
|
||||||
"infisical-node": "^1.0.37",
|
"infisical-node": "^1.0.37",
|
||||||
"ip": "^2.0.1",
|
"ip": "^2.0.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.2",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"lottie-react": "^2.4.0",
|
"lottie-react": "^2.4.0",
|
||||||
|
@ -25,6 +25,21 @@ export const UserProvider = ({ children }: Props): JSX.Element => {
|
|||||||
};
|
};
|
||||||
}, [data, isLoading]);
|
}, [data, isLoading]);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
|
||||||
|
<img
|
||||||
|
src="/images/loading/loading.gif"
|
||||||
|
height={70}
|
||||||
|
width={120}
|
||||||
|
decoding="async"
|
||||||
|
loading="lazy"
|
||||||
|
alt="infisical loading indicator"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
|
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
|||||||
[EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG]:
|
[EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG]:
|
||||||
"Update certificate template EST configuration",
|
"Update certificate template EST configuration",
|
||||||
[EventType.UPDATE_PROJECT_SLACK_CONFIG]: "Update project slack configuration",
|
[EventType.UPDATE_PROJECT_SLACK_CONFIG]: "Update project slack configuration",
|
||||||
[EventType.GET_PROJECT_SLACK_CONFIG]: "Get project slack configuration"
|
[EventType.GET_PROJECT_SLACK_CONFIG]: "Get project slack configuration",
|
||||||
|
[EventType.INTEGRATION_SYNCED]: "Integration sync"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
|
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export enum ActorType {
|
export enum ActorType {
|
||||||
|
PLATFORM = "platform",
|
||||||
USER = "user",
|
USER = "user",
|
||||||
SERVICE = "service",
|
SERVICE = "service",
|
||||||
IDENTITY = "identity"
|
IDENTITY = "identity"
|
||||||
@ -91,5 +92,6 @@ export enum EventType {
|
|||||||
UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "update-certificate-template-est-config",
|
UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "update-certificate-template-est-config",
|
||||||
GET_CERTIFICATE_TEMPLATE_EST_CONFIG = "get-certificate-template-est-config",
|
GET_CERTIFICATE_TEMPLATE_EST_CONFIG = "get-certificate-template-est-config",
|
||||||
UPDATE_PROJECT_SLACK_CONFIG = "update-project-slack-config",
|
UPDATE_PROJECT_SLACK_CONFIG = "update-project-slack-config",
|
||||||
GET_PROJECT_SLACK_CONFIG = "get-project-slack-config"
|
GET_PROJECT_SLACK_CONFIG = "get-project-slack-config",
|
||||||
|
INTEGRATION_SYNCED = "integration-synced"
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,58 @@
|
|||||||
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery, UseInfiniteQueryOptions, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { Actor, AuditLog, AuditLogFilters } from "./types";
|
import { Actor, AuditLog, TGetAuditLogsFilter } from "./types";
|
||||||
|
|
||||||
export const auditLogKeys = {
|
export const auditLogKeys = {
|
||||||
getAuditLogs: (workspaceId: string | null, filters: AuditLogFilters) =>
|
getAuditLogs: (workspaceId: string | null, filters: TGetAuditLogsFilter) =>
|
||||||
[{ workspaceId, filters }, "audit-logs"] as const,
|
[{ workspaceId, filters }, "audit-logs"] as const,
|
||||||
getAuditLogActorFilterOpts: (workspaceId: string) =>
|
getAuditLogActorFilterOpts: (workspaceId: string) =>
|
||||||
[{ workspaceId }, "audit-log-actor-filters"] as const
|
[{ workspaceId }, "audit-log-actor-filters"] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetAuditLogs = (filters: AuditLogFilters, workspaceId: string | null) => {
|
export const useGetAuditLogs = (
|
||||||
|
filters: TGetAuditLogsFilter,
|
||||||
|
projectId: string | null,
|
||||||
|
options: Omit<
|
||||||
|
UseInfiniteQueryOptions<
|
||||||
|
AuditLog[],
|
||||||
|
unknown,
|
||||||
|
AuditLog[],
|
||||||
|
AuditLog[],
|
||||||
|
ReturnType<typeof auditLogKeys.getAuditLogs>
|
||||||
|
>,
|
||||||
|
"queryFn" | "queryKey" | "getNextPageParam"
|
||||||
|
> = {}
|
||||||
|
) => {
|
||||||
return useInfiniteQuery({
|
return useInfiniteQuery({
|
||||||
queryKey: auditLogKeys.getAuditLogs(workspaceId, filters),
|
queryKey: auditLogKeys.getAuditLogs(projectId, filters),
|
||||||
queryFn: async ({ pageParam }) => {
|
queryFn: async ({ pageParam }) => {
|
||||||
const auditLogEndpoint = workspaceId
|
const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>(
|
||||||
? `/api/v1/workspace/${workspaceId}/audit-logs`
|
"/api/v1/organization/audit-logs",
|
||||||
: "/api/v1/organization/audit-logs";
|
{
|
||||||
const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>(auditLogEndpoint, {
|
params: {
|
||||||
params: {
|
...filters,
|
||||||
...filters,
|
offset: pageParam,
|
||||||
offset: pageParam,
|
startDate: filters?.startDate?.toISOString(),
|
||||||
startDate: filters?.startDate?.toISOString(),
|
endDate: filters?.endDate?.toISOString(),
|
||||||
endDate: filters?.endDate?.toISOString()
|
...(filters.eventMetadata && Object.keys(filters.eventMetadata).length
|
||||||
|
? {
|
||||||
|
eventMetadata: Object.entries(filters.eventMetadata)
|
||||||
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
|
.join(",")
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(filters.eventType?.length ? { eventType: filters.eventType.join(",") } : {}),
|
||||||
|
...(projectId ? { projectId } : {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
return data.auditLogs;
|
return data.auditLogs;
|
||||||
},
|
},
|
||||||
getNextPageParam: (lastPage, pages) =>
|
getNextPageParam: (lastPage, pages) =>
|
||||||
lastPage.length !== 0 ? pages.length * filters.limit : undefined
|
lastPage.length !== 0 ? pages.length * filters.limit : undefined,
|
||||||
|
...options
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,17 @@ import { IdentityTrustedIp } from "../identities/types";
|
|||||||
import { PkiItemType } from "../pkiCollections/constants";
|
import { PkiItemType } from "../pkiCollections/constants";
|
||||||
import { ActorType, EventType, UserAgentType } from "./enums";
|
import { ActorType, EventType, UserAgentType } from "./enums";
|
||||||
|
|
||||||
|
export type TGetAuditLogsFilter = {
|
||||||
|
eventType?: EventType[];
|
||||||
|
userAgentType?: UserAgentType;
|
||||||
|
eventMetadata?: Record<string, string>;
|
||||||
|
actorType?: ActorType;
|
||||||
|
actorId?: string; // user ID format
|
||||||
|
startDate?: Date;
|
||||||
|
endDate?: Date;
|
||||||
|
limit: number;
|
||||||
|
};
|
||||||
|
|
||||||
interface UserActorMetadata {
|
interface UserActorMetadata {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
@ -33,7 +44,13 @@ export interface IdentityActor {
|
|||||||
metadata: IdentityActorMetadata;
|
metadata: IdentityActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actor = UserActor | ServiceActor | IdentityActor;
|
export interface PlatformActorMetadata {}
|
||||||
|
export interface PlatformActor {
|
||||||
|
type: ActorType.PLATFORM;
|
||||||
|
metadata: PlatformActorMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Actor = UserActor | ServiceActor | IdentityActor | PlatformActor;
|
||||||
|
|
||||||
interface GetSecretsEvent {
|
interface GetSecretsEvent {
|
||||||
type: EventType.GET_SECRETS;
|
type: EventType.GET_SECRETS;
|
||||||
@ -761,6 +778,22 @@ interface GetProjectSlackConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum IntegrationSyncedEventTrigger {
|
||||||
|
MANUAL = "manual",
|
||||||
|
AUTO = "auto"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IntegrationSyncedEvent {
|
||||||
|
type: EventType.INTEGRATION_SYNCED;
|
||||||
|
metadata: {
|
||||||
|
integrationId: string;
|
||||||
|
lastSyncJobId: string;
|
||||||
|
lastUsed: Date;
|
||||||
|
syncMessage: string;
|
||||||
|
isSynced: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@ -838,7 +871,8 @@ export type Event =
|
|||||||
| CreateCertificateTemplateEstConfig
|
| CreateCertificateTemplateEstConfig
|
||||||
| GetCertificateTemplateEstConfig
|
| GetCertificateTemplateEstConfig
|
||||||
| UpdateProjectSlackConfig
|
| UpdateProjectSlackConfig
|
||||||
| GetProjectSlackConfig;
|
| GetProjectSlackConfig
|
||||||
|
| IntegrationSyncedEvent;
|
||||||
|
|
||||||
export type AuditLog = {
|
export type AuditLog = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -856,12 +890,3 @@ export type AuditLog = {
|
|||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AuditLogFilters = {
|
|
||||||
eventType?: EventType;
|
|
||||||
userAgentType?: UserAgentType;
|
|
||||||
actor?: string;
|
|
||||||
limit: number;
|
|
||||||
startDate?: Date;
|
|
||||||
endDate?: Date;
|
|
||||||
};
|
|
||||||
|
@ -1 +1,6 @@
|
|||||||
export { useCreateIntegration, useDeleteIntegration, useGetCloudIntegrations } from "./queries";
|
export {
|
||||||
|
useCreateIntegration,
|
||||||
|
useDeleteIntegration,
|
||||||
|
useGetCloudIntegrations,
|
||||||
|
useGetIntegration
|
||||||
|
} from "./queries";
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { workspaceKeys } from "../workspace";
|
import { workspaceKeys } from "../workspace";
|
||||||
import { TCloudIntegration } from "./types";
|
import { TCloudIntegration, TIntegrationWithEnv } from "./types";
|
||||||
|
|
||||||
export const integrationQueryKeys = {
|
export const integrationQueryKeys = {
|
||||||
getIntegrations: () => ["integrations"] as const
|
getIntegrations: () => ["integrations"] as const,
|
||||||
|
getIntegration: (id: string) => ["integration", id] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrations = async () => {
|
const fetchIntegrations = async () => {
|
||||||
@ -18,6 +19,14 @@ const fetchIntegrations = async () => {
|
|||||||
return data.integrationOptions;
|
return data.integrationOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchIntegration = async (id: string) => {
|
||||||
|
const { data } = await apiRequest.get<{ integration: TIntegrationWithEnv }>(
|
||||||
|
`/api/v1/integration/${id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.integration;
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetCloudIntegrations = () =>
|
export const useGetCloudIntegrations = () =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: integrationQueryKeys.getIntegrations(),
|
queryKey: integrationQueryKeys.getIntegrations(),
|
||||||
@ -128,6 +137,26 @@ export const useDeleteIntegration = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetIntegration = (
|
||||||
|
integrationId: string,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<
|
||||||
|
TIntegrationWithEnv,
|
||||||
|
unknown,
|
||||||
|
TIntegrationWithEnv,
|
||||||
|
ReturnType<typeof integrationQueryKeys.getIntegration>
|
||||||
|
>,
|
||||||
|
"queryFn" | "queryKey"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(integrationId && options?.enabled === undefined ? true : options?.enabled),
|
||||||
|
queryKey: integrationQueryKeys.getIntegration(integrationId),
|
||||||
|
queryFn: () => fetchIntegration(integrationId)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useSyncIntegration = () => {
|
export const useSyncIntegration = () => {
|
||||||
return useMutation<{}, {}, { id: string; workspaceId: string; lastUsed: string }>({
|
return useMutation<{}, {}, { id: string; workspaceId: string; lastUsed: string }>({
|
||||||
mutationFn: ({ id }) => apiRequest.post(`/api/v1/integration/${id}/sync`),
|
mutationFn: ({ id }) => apiRequest.post(`/api/v1/integration/${id}/sync`),
|
||||||
|
@ -36,14 +36,34 @@ export type TIntegration = {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
githubVisibility?: string;
|
githubVisibility?: string;
|
||||||
githubVisibilityRepoIds?: string[];
|
githubVisibilityRepoIds?: string[];
|
||||||
|
shouldAutoRedeploy?: boolean;
|
||||||
|
secretAWSTag?: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
kmsKeyId?: string;
|
||||||
secretSuffix?: string;
|
secretSuffix?: string;
|
||||||
|
secretPrefix?: string;
|
||||||
syncBehavior?: IntegrationSyncBehavior;
|
syncBehavior?: IntegrationSyncBehavior;
|
||||||
mappingBehavior?: IntegrationMappingBehavior;
|
mappingBehavior?: IntegrationMappingBehavior;
|
||||||
scope: string;
|
scope: string;
|
||||||
org: string;
|
org: string;
|
||||||
project: string;
|
project: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
|
||||||
|
shouldDisableDelete?: boolean;
|
||||||
|
shouldMaskSecrets?: boolean;
|
||||||
|
shouldProtectSecrets?: boolean;
|
||||||
|
shouldEnableDelete?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TIntegrationWithEnv = TIntegration & {
|
||||||
|
environment: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
117
frontend/src/layouts/AppLayout/ErrorBoundary.tsx
Normal file
117
frontend/src/layouts/AppLayout/ErrorBoundary.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import React, { ErrorInfo, ReactNode, useEffect } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { faBugs, faHome } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { Button } from "@app/components/v2";
|
||||||
|
|
||||||
|
interface ErrorBoundaryProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorBoundaryState {
|
||||||
|
hasError: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorPage = ({ error }: { error: Error | null }) => {
|
||||||
|
const [orgId, setOrgId] = React.useState<string | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
const currentUrl = router?.asPath?.split("?")?.[0];
|
||||||
|
|
||||||
|
// Workaround: Fixes localStorage not being available in the error boundary until the next render.
|
||||||
|
useEffect(() => {
|
||||||
|
const savedOrgId = localStorage.getItem("orgData.id");
|
||||||
|
|
||||||
|
if (savedOrgId) {
|
||||||
|
setOrgId(savedOrgId);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-screen items-center justify-center bg-mineshaft-900">
|
||||||
|
<div className="flex max-w-md flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-8 text-center text-mineshaft-200">
|
||||||
|
<FontAwesomeIcon icon={faBugs} className="my-2 inline text-6xl" />
|
||||||
|
<p>
|
||||||
|
Something went wrong. Please contact{" "}
|
||||||
|
<a
|
||||||
|
className="inline cursor-pointer text-mineshaft-100 underline decoration-primary-500 underline-offset-4 opacity-80 duration-200 hover:opacity-100"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="mailto:support@infisical.com"
|
||||||
|
>
|
||||||
|
support@infisical.com
|
||||||
|
</a>
|
||||||
|
, or{" "}
|
||||||
|
<Link passHref href="https://infisical.com/slack">
|
||||||
|
<a
|
||||||
|
className="inline cursor-pointer text-mineshaft-100 underline decoration-primary-500 underline-offset-4 opacity-80 duration-200 hover:opacity-100"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
join our Slack community
|
||||||
|
</a>
|
||||||
|
</Link>{" "}
|
||||||
|
if the issue persists.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{orgId && (
|
||||||
|
<Button
|
||||||
|
className="mt-4"
|
||||||
|
size="xs"
|
||||||
|
onClick={() =>
|
||||||
|
// we need to go to /org/${orgId}/overview, but we need to do a full page reload to ensure that the error the user is facing is properly reset.
|
||||||
|
window.location.assign(`/org/${orgId}/overview`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faHome} className="mr-2" />
|
||||||
|
Back To Home
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{error?.message && (
|
||||||
|
<>
|
||||||
|
<div className="my-4 h-px w-full bg-mineshaft-600" />
|
||||||
|
<p className="thin-scrollbar max-h-44 w-full overflow-auto text-ellipsis rounded-md bg-mineshaft-700 p-2">
|
||||||
|
<code className="text-xs">
|
||||||
|
{currentUrl}, {error.message}
|
||||||
|
</code>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||||
|
constructor(props: ErrorBoundaryProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = { hasError: false, error: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||||
|
return { hasError: true, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||||
|
console.error("Error caught by ErrorBoundary:", error, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): ReactNode {
|
||||||
|
const { hasError, error } = this.state;
|
||||||
|
const { children } = this.props;
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
return <ErrorPage error={error} />;
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorBoundaryWrapper = ({ children }: ErrorBoundaryProps) => {
|
||||||
|
return <ErrorBoundary>{children}</ErrorBoundary>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorBoundaryWrapper;
|
@ -27,6 +27,7 @@ import {
|
|||||||
WorkspaceProvider
|
WorkspaceProvider
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { AppLayout } from "@app/layouts";
|
import { AppLayout } from "@app/layouts";
|
||||||
|
import ErrorBoundaryWrapper from "@app/layouts/AppLayout/ErrorBoundary";
|
||||||
import { queryClient } from "@app/reactQuery";
|
import { queryClient } from "@app/reactQuery";
|
||||||
|
|
||||||
import "nprogress/nprogress.css";
|
import "nprogress/nprogress.css";
|
||||||
@ -85,46 +86,50 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
|
|||||||
!Component.requireAuth
|
!Component.requireAuth
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<ErrorBoundaryWrapper>
|
||||||
<NotificationContainer />
|
<QueryClientProvider client={queryClient}>
|
||||||
<ServerConfigProvider>
|
<NotificationContainer />
|
||||||
<UserProvider>
|
<ServerConfigProvider>
|
||||||
<AuthProvider>
|
<UserProvider>
|
||||||
<Component {...pageProps} />
|
<AuthProvider>
|
||||||
</AuthProvider>
|
<Component {...pageProps} />
|
||||||
</UserProvider>
|
</AuthProvider>
|
||||||
</ServerConfigProvider>
|
</UserProvider>
|
||||||
</QueryClientProvider>
|
</ServerConfigProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</ErrorBoundaryWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = Component?.layout || AppLayout;
|
const Layout = Component?.layout || AppLayout;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<ErrorBoundaryWrapper>
|
||||||
<TooltipProvider>
|
<QueryClientProvider client={queryClient}>
|
||||||
<NotificationContainer />
|
<TooltipProvider>
|
||||||
<ServerConfigProvider>
|
<NotificationContainer />
|
||||||
<AuthProvider>
|
<ServerConfigProvider>
|
||||||
<OrgProvider>
|
<AuthProvider>
|
||||||
<OrgPermissionProvider>
|
<OrgProvider>
|
||||||
<WorkspaceProvider>
|
<OrgPermissionProvider>
|
||||||
<ProjectPermissionProvider>
|
<WorkspaceProvider>
|
||||||
<SubscriptionProvider>
|
<ProjectPermissionProvider>
|
||||||
<UserProvider>
|
<SubscriptionProvider>
|
||||||
<Layout>
|
<UserProvider>
|
||||||
<Component {...pageProps} />
|
<Layout>
|
||||||
</Layout>
|
<Component {...pageProps} />
|
||||||
</UserProvider>
|
</Layout>
|
||||||
</SubscriptionProvider>
|
</UserProvider>
|
||||||
</ProjectPermissionProvider>
|
</SubscriptionProvider>
|
||||||
</WorkspaceProvider>
|
</ProjectPermissionProvider>
|
||||||
</OrgPermissionProvider>
|
</WorkspaceProvider>
|
||||||
</OrgProvider>
|
</OrgPermissionProvider>
|
||||||
</AuthProvider>
|
</OrgProvider>
|
||||||
</ServerConfigProvider>
|
</AuthProvider>
|
||||||
</TooltipProvider>
|
</ServerConfigProvider>
|
||||||
</QueryClientProvider>
|
</TooltipProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</ErrorBoundaryWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
23
frontend/src/pages/integrations/details/[integrationId].tsx
Normal file
23
frontend/src/pages/integrations/details/[integrationId].tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Head from "next/head";
|
||||||
|
|
||||||
|
import { IntegrationDetailsPage } from "@app/views/IntegrationsPage/IntegrationDetailsPage";
|
||||||
|
|
||||||
|
export default function IntegrationsDetailsPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Integration Details | Infisical</title>
|
||||||
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
<meta property="og:image" content="/images/message.png" />
|
||||||
|
<meta property="og:title" content="Manage your .env files in seconds" />
|
||||||
|
<meta name="og:description" content={t("integrations.description") as string} />
|
||||||
|
</Head>
|
||||||
|
<IntegrationDetailsPage />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegrationsDetailsPage.requireAuth = true;
|
@ -0,0 +1,120 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { faChevronLeft, faEllipsis, faRefresh, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
OrgPermissionActions,
|
||||||
|
OrgPermissionSubjects,
|
||||||
|
useOrganization,
|
||||||
|
useUser,
|
||||||
|
useWorkspace
|
||||||
|
} from "@app/context";
|
||||||
|
import { useGetIntegration } from "@app/hooks/api";
|
||||||
|
import { useSyncIntegration } from "@app/hooks/api/integrations/queries";
|
||||||
|
|
||||||
|
import { IntegrationAuditLogsSection } from "./components/IntegrationAuditLogsSection";
|
||||||
|
import { IntegrationConnectionSection } from "./components/IntegrationConnectionSection";
|
||||||
|
import { IntegrationDetailsSection } from "./components/IntegrationDetailsSection";
|
||||||
|
import { IntegrationSettingsSection } from "./components/IntegrationSettingsSection";
|
||||||
|
|
||||||
|
export const IntegrationDetailsPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const integrationId = router.query.integrationId as string;
|
||||||
|
|
||||||
|
const { data: integration } = useGetIntegration(integrationId, {
|
||||||
|
refetchInterval: 4000
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectId = useWorkspace().currentWorkspace?.id;
|
||||||
|
const { mutateAsync: syncIntegration } = useSyncIntegration();
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
|
||||||
|
return integration ? (
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
type="submit"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/integrations/${projectId}`);
|
||||||
|
}}
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
Integrations
|
||||||
|
</Button>
|
||||||
|
<div className="mb-4 flex items-center justify-between">
|
||||||
|
<p className="text-3xl font-semibold text-white">
|
||||||
|
{integrationSlugNameMapping[integration.integration]} Integration
|
||||||
|
</p>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
|
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||||
|
<Tooltip content="More options">
|
||||||
|
<FontAwesomeIcon size="sm" icon={faEllipsis} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
await syncIntegration({
|
||||||
|
id: integration.id,
|
||||||
|
lastUsed: integration.lastUsed!,
|
||||||
|
workspaceId: projectId!
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FontAwesomeIcon icon={faRefresh} />
|
||||||
|
Manually Sync
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<OrgPermissionCan I={OrgPermissionActions.Delete} a={OrgPermissionSubjects.Member}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className={twMerge(
|
||||||
|
isAllowed
|
||||||
|
? "hover:!bg-red-500 hover:!text-white"
|
||||||
|
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||||
|
)}
|
||||||
|
onClick={() => {}}
|
||||||
|
disabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
|
Delete Integration
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="mr-4 w-96">
|
||||||
|
<IntegrationDetailsSection integration={integration} />
|
||||||
|
<IntegrationConnectionSection integration={integration} />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<IntegrationSettingsSection integration={integration} />
|
||||||
|
<IntegrationAuditLogsSection orgId={currentOrg?.id || ""} integration={integration} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
};
|
@ -0,0 +1,78 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { EmptyState } from "@app/components/v2";
|
||||||
|
import { useSubscription } from "@app/context";
|
||||||
|
import { EventType } from "@app/hooks/api/auditLogs/enums";
|
||||||
|
import { TIntegrationWithEnv } from "@app/hooks/api/integrations/types";
|
||||||
|
import { LogsSection } from "@app/views/Project/AuditLogsPage/components";
|
||||||
|
|
||||||
|
// Add more events if needed
|
||||||
|
const INTEGRATION_EVENTS = [EventType.INTEGRATION_SYNCED];
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
integration: TIntegrationWithEnv;
|
||||||
|
orgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IntegrationAuditLogsSection = ({ integration, orgId }: Props) => {
|
||||||
|
const { subscription, isLoading } = useSubscription();
|
||||||
|
|
||||||
|
const auditLogsRetentionDays = subscription?.auditLogsRetentionDays ?? 30;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
return subscription?.auditLogs ? (
|
||||||
|
<div className="h-full w-full min-w-[51rem] rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="mb-4 flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<p className="text-lg font-semibold text-gray-200">Integration Logs</p>
|
||||||
|
<p className="text-xs text-gray-400">
|
||||||
|
Displaying audit logs from the last {auditLogsRetentionDays} days
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<LogsSection
|
||||||
|
refetchInterval={4000}
|
||||||
|
remappedHeaders={{
|
||||||
|
Metadata: "Sync Status"
|
||||||
|
}}
|
||||||
|
showFilters={false}
|
||||||
|
presets={{
|
||||||
|
eventMetadata: { integrationId: integration.id },
|
||||||
|
startDate: new Date(new Date().setDate(new Date().getDate() - auditLogsRetentionDays)),
|
||||||
|
eventType: INTEGRATION_EVENTS
|
||||||
|
}}
|
||||||
|
filterClassName="bg-mineshaft-900 static"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : !isLoading ? (
|
||||||
|
<div className="h-full w-full min-w-[51rem] rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4 opacity-60">
|
||||||
|
<div className="mb-4 flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<p className="text-lg font-semibold text-gray-200">Integration Logs</p>
|
||||||
|
</div>
|
||||||
|
<EmptyState
|
||||||
|
className="rounded-lg"
|
||||||
|
title={
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Please{" "}
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
subscription && subscription.slug !== null
|
||||||
|
? `/org${orgId}/billing`
|
||||||
|
: "https://infisical.com/scheduledemo"
|
||||||
|
}
|
||||||
|
passHref
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
className="cursor-pointer font-medium text-primary-500 transition-all hover:text-primary-600"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
upgrade your subscription
|
||||||
|
</a>
|
||||||
|
</Link>{" "}
|
||||||
|
to view integration logs
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
};
|
@ -0,0 +1,194 @@
|
|||||||
|
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||||
|
|
||||||
|
import { FormLabel } from "@app/components/v2";
|
||||||
|
import { IntegrationMappingBehavior, TIntegrationWithEnv } from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
integration: TIntegrationWithEnv;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IntegrationConnectionSection = ({ integration }: Props) => {
|
||||||
|
const specifcQoveryDetails = () => {
|
||||||
|
if (integration.integration !== "qovery") return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Organization" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration?.owner || "-"}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Project" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration?.targetService || "-"}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel
|
||||||
|
className="text-sm font-semibold text-mineshaft-300"
|
||||||
|
label="Target Environment"
|
||||||
|
/>
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration?.targetEnvironment || "-"}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNotAwsManagerOneToOneDetails = () => {
|
||||||
|
const isAwsSecretManagerOneToOne =
|
||||||
|
integration.integration === "aws-secret-manager" &&
|
||||||
|
integration.metadata?.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE;
|
||||||
|
|
||||||
|
if (isAwsSecretManagerOneToOne) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formLabel = () => {
|
||||||
|
switch (integration.integration) {
|
||||||
|
case "qovery":
|
||||||
|
return integration.scope;
|
||||||
|
case "circleci":
|
||||||
|
case "terraform-cloud":
|
||||||
|
return "Project";
|
||||||
|
case "aws-secret-manager":
|
||||||
|
return "Secret";
|
||||||
|
case "aws-parameter-store":
|
||||||
|
case "rundeck":
|
||||||
|
return "Path";
|
||||||
|
case "github":
|
||||||
|
if (["github-env", "github-repo"].includes(integration.scope!)) {
|
||||||
|
return "Repository";
|
||||||
|
}
|
||||||
|
return "Organization";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "App";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const contents = () => {
|
||||||
|
switch (integration.integration) {
|
||||||
|
case "hashicorp-vault":
|
||||||
|
return `${integration.app} - path: ${integration.path}`;
|
||||||
|
case "github":
|
||||||
|
if (integration.scope === "github-org") {
|
||||||
|
return `${integration.owner}`;
|
||||||
|
}
|
||||||
|
return `${integration.owner}/${integration.app}`;
|
||||||
|
|
||||||
|
case "aws-parameter-store":
|
||||||
|
case "rundeck":
|
||||||
|
return `${integration.path}`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `${integration.app}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label={formLabel()} />
|
||||||
|
<div className="text-sm text-mineshaft-300">{contents()}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const targetEnvironmentDetails = () => {
|
||||||
|
if (
|
||||||
|
["vercel", "netlify", "railway", "gitlab", "teamcity", "bitbucket"].includes(
|
||||||
|
integration.integration
|
||||||
|
) ||
|
||||||
|
(integration.integration === "github" && integration.scope === "github-env")
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel
|
||||||
|
className="text-sm font-semibold text-mineshaft-300"
|
||||||
|
label="Target Environment"
|
||||||
|
/>
|
||||||
|
<div className="text-sm text-mineshaft-300">
|
||||||
|
{integration.targetEnvironment || integration.targetEnvironmentId}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generalIntegrationSpecificDetails = () => {
|
||||||
|
if (integration.integration === "checkly" && integration.targetService) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Group" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.targetService}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.integration === "circleci" && integration.owner) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Organization" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.owner}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.integration === "terraform-cloud" && integration.targetService) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Category" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.targetService}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.integration === "checkly" || integration.integration === "github") {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Secret Suffix" />
|
||||||
|
<div className="text-sm text-mineshaft-300">
|
||||||
|
{integration?.metadata?.secretSuffix || "-"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<h3 className="text-lg font-semibold text-mineshaft-100">Connection</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<FormLabel className="my-2" label="Source" />
|
||||||
|
|
||||||
|
<div className="space-y-2 rounded-lg border border-mineshaft-700 bg-mineshaft-800 p-2">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Environment" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.environment.name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Secret Path" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.secretPath}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormLabel className="my-2" label="Destination" />
|
||||||
|
<div className="space-y-2 rounded-lg border border-mineshaft-700 bg-mineshaft-800 p-2">
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Platform" />
|
||||||
|
<div className="text-sm text-mineshaft-300">
|
||||||
|
{integrationSlugNameMapping[integration.integration]}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{specifcQoveryDetails()}
|
||||||
|
{isNotAwsManagerOneToOneDetails()}
|
||||||
|
{targetEnvironmentDetails()}
|
||||||
|
{generalIntegrationSpecificDetails()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,69 @@
|
|||||||
|
import { faCalendarCheck, faCheckCircle, faCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { TIntegrationWithEnv } from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
integration: TIntegrationWithEnv;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IntegrationDetailsSection = ({ integration }: Props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<h3 className="text-lg font-semibold text-mineshaft-100">Integration Details</h3>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
|
||||||
|
<p className="text-sm text-mineshaft-300">
|
||||||
|
{integrationSlugNameMapping[integration.integration]}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Sync Status</p>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<p
|
||||||
|
className={twMerge(
|
||||||
|
"mr-2 text-sm font-medium",
|
||||||
|
integration.isSynced ? "text-green-500" : "text-red-500"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{integration.isSynced ? "Synced" : "Not Synced"}
|
||||||
|
</p>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
size="sm"
|
||||||
|
className={twMerge(integration.isSynced ? "text-green-500" : "text-red-500")}
|
||||||
|
icon={integration.isSynced ? faCheckCircle : faCircleXmark}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{integration.lastUsed && (
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Latest Successful Sync</p>
|
||||||
|
<div className="flex items-center gap-2 text-sm text-mineshaft-300">
|
||||||
|
{format(new Date(integration.lastUsed), "yyyy-MM-dd, hh:mm aaa")}
|
||||||
|
<FontAwesomeIcon icon={faCalendarCheck} className="pt-0.5 pr-2 text-sm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{!integration.isSynced && integration.syncMessage && (
|
||||||
|
<>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Latest Sync Error</p>
|
||||||
|
<p className="text-sm text-mineshaft-300">{integration.syncMessage}</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,89 @@
|
|||||||
|
import { TIntegrationWithEnv } from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
integration: TIntegrationWithEnv;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Metadata = NonNullable<TIntegrationWithEnv["metadata"]>;
|
||||||
|
type MetadataKey = keyof Metadata;
|
||||||
|
type MetadataValue<K extends MetadataKey> = Metadata[K];
|
||||||
|
|
||||||
|
const metadataMappings: Record<keyof NonNullable<TIntegrationWithEnv["metadata"]>, string> = {
|
||||||
|
githubVisibility: "Github Visibility",
|
||||||
|
githubVisibilityRepoIds: "Github Visibility Repo Ids",
|
||||||
|
shouldAutoRedeploy: "Auto Redeploy Target Application When Secrets Change",
|
||||||
|
secretAWSTag: "Tags For Secrets Stored In AWS",
|
||||||
|
kmsKeyId: "AWS KMS Key ID",
|
||||||
|
secretSuffix: "Secret Suffix",
|
||||||
|
secretPrefix: "Secret Prefix",
|
||||||
|
syncBehavior: "Secrets Sync behavior",
|
||||||
|
mappingBehavior: "Secrets Mapping Behavior",
|
||||||
|
scope: "Scope",
|
||||||
|
org: "Organization",
|
||||||
|
project: "Project",
|
||||||
|
environment: "Environment",
|
||||||
|
shouldDisableDelete: "AWS Secret Deletion Disabled",
|
||||||
|
shouldMaskSecrets: "GitLab Secrets Masking Enabled",
|
||||||
|
shouldProtectSecrets: "GitLab Secret Protection Enabled",
|
||||||
|
shouldEnableDelete: "GitHub Secret Deletion Enabled"
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const IntegrationSettingsSection = ({ integration }: Props) => {
|
||||||
|
const renderValue = <K extends MetadataKey>(key: K, value: MetadataValue<K>) => {
|
||||||
|
if (!value) return null;
|
||||||
|
|
||||||
|
// If it's a boolean, we render a generic "Yes" or "No" response.
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return value ? "Yes" : "No";
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the value is an object or array, or array of objects, we need to handle some special cases.
|
||||||
|
if (typeof value === "object") {
|
||||||
|
if (key === "secretAWSTag") {
|
||||||
|
return (value as MetadataValue<"secretAWSTag">)!.map(({ key: tagKey, value: tagValue }) => (
|
||||||
|
<p key={tagKey} className="text-sm text-gray-200">
|
||||||
|
{tagKey}={tagValue}
|
||||||
|
</p>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === "githubVisibilityRepoIds") {
|
||||||
|
return value.join(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return value.length ? value : "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!integration.metadata || Object.keys(integration.metadata).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
return (
|
||||||
|
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="mb-4 flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<p className="text-lg font-semibold text-gray-200">Integration Settings</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{integration.metadata &&
|
||||||
|
Object.entries(integration.metadata).map(([key, value]) => (
|
||||||
|
<div key={key} className="flex flex-col">
|
||||||
|
<p className="text-sm text-gray-400">
|
||||||
|
{metadataMappings[key as keyof typeof metadataMappings]}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-200">{renderValue(key as MetadataKey, value)}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export { IntegrationDetailsPage } from "./IntegrationDetailsPage";
|
@ -1,6 +1,10 @@
|
|||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import {
|
import {
|
||||||
faArrowRight,
|
faArrowRight,
|
||||||
faCalendarCheck,
|
faCalendarCheck,
|
||||||
|
faEllipsis,
|
||||||
faRefresh,
|
faRefresh,
|
||||||
faWarning,
|
faWarning,
|
||||||
faXmark
|
faXmark
|
||||||
@ -10,7 +14,7 @@ import { format } from "date-fns";
|
|||||||
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { Button, FormLabel, IconButton, Tag, Tooltip } from "@app/components/v2";
|
import { Badge, FormLabel, IconButton, Tooltip } from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
|
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
|
||||||
import { TIntegration } from "@app/hooks/api/types";
|
import { TIntegration } from "@app/hooks/api/types";
|
||||||
@ -28,9 +32,12 @@ export const ConfiguredIntegrationItem = ({
|
|||||||
onRemoveIntegration,
|
onRemoveIntegration,
|
||||||
onManualSyncIntegration
|
onManualSyncIntegration
|
||||||
}: IProps) => {
|
}: IProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="max-w-8xl flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3"
|
className="max-w-8xl flex cursor-pointer justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3 transition-all hover:bg-mineshaft-700"
|
||||||
|
onClick={() => router.push(`/integrations/details/${integration.id}`)}
|
||||||
key={`integration-${integration?.id.toString()}`}
|
key={`integration-${integration?.id.toString()}`}
|
||||||
>
|
>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@ -168,9 +175,9 @@ export const ConfiguredIntegrationItem = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-[1.5rem] flex cursor-default">
|
<div className="mt-[1.5rem] flex cursor-default space-x-3">
|
||||||
{integration.isSynced != null && integration.lastUsed != null && (
|
{integration.isSynced != null && integration.lastUsed != null && (
|
||||||
<Tag key={integration.id} className={integration.isSynced ? "bg-green-800" : "bg-red/80"}>
|
<Badge variant={integration.isSynced ? "success" : "danger"} key={integration.id}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
center
|
center
|
||||||
className="max-w-xs whitespace-normal break-words"
|
className="max-w-xs whitespace-normal break-words"
|
||||||
@ -178,7 +185,7 @@ export const ConfiguredIntegrationItem = ({
|
|||||||
<div className="flex max-h-[10rem] flex-col overflow-auto ">
|
<div className="flex max-h-[10rem] flex-col overflow-auto ">
|
||||||
<div className="flex self-start">
|
<div className="flex self-start">
|
||||||
<FontAwesomeIcon icon={faCalendarCheck} className="pt-0.5 pr-2 text-sm" />
|
<FontAwesomeIcon icon={faCalendarCheck} className="pt-0.5 pr-2 text-sm" />
|
||||||
<div className="text-sm">Last sync</div>
|
<div className="text-sm">Last successful sync</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-5 text-left text-xs">
|
<div className="pl-5 text-left text-xs">
|
||||||
{format(new Date(integration.lastUsed), "yyyy-MM-dd, hh:mm aaa")}
|
{format(new Date(integration.lastUsed), "yyyy-MM-dd, hh:mm aaa")}
|
||||||
@ -195,43 +202,62 @@ export const ConfiguredIntegrationItem = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2 text-white">
|
<div className="flex h-full items-center space-x-2">
|
||||||
<div>{integration.isSynced ? "Synced" : "Not synced"}</div>
|
<div>{integration.isSynced ? "Synced" : "Not synced"}</div>
|
||||||
{!integration.isSynced && <FontAwesomeIcon icon={faWarning} />}
|
{!integration.isSynced && <FontAwesomeIcon icon={faWarning} />}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Tag>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<div className="mr-1 flex items-end opacity-80 duration-200 hover:opacity-100">
|
<div className="space-x-1.5">
|
||||||
<Tooltip className="text-center" content="Manually sync integration secrets">
|
<Tooltip className="text-center" content="Manually sync integration secrets">
|
||||||
<Button
|
<IconButton
|
||||||
onClick={() => onManualSyncIntegration()}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onManualSyncIntegration();
|
||||||
|
}}
|
||||||
|
ariaLabel="sync"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="star"
|
||||||
className="max-w-[2.5rem] border-none bg-mineshaft-500"
|
className="max-w-[2.5rem] border-none bg-mineshaft-500"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faRefresh} className="px-1 text-bunker-200" />
|
<FontAwesomeIcon icon={faRefresh} className="px-1" />
|
||||||
</Button>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
<ProjectPermissionCan
|
||||||
<ProjectPermissionCan
|
I={ProjectPermissionActions.Delete}
|
||||||
I={ProjectPermissionActions.Delete}
|
a={ProjectPermissionSub.Integrations}
|
||||||
a={ProjectPermissionSub.Integrations}
|
>
|
||||||
>
|
{(isAllowed: boolean) => (
|
||||||
{(isAllowed: boolean) => (
|
|
||||||
<div className="flex items-end opacity-80 duration-200 hover:opacity-100">
|
|
||||||
<Tooltip content="Remove Integration">
|
<Tooltip content="Remove Integration">
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => onRemoveIntegration()}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onRemoveIntegration();
|
||||||
|
}}
|
||||||
ariaLabel="delete"
|
ariaLabel="delete"
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="star"
|
variant="star"
|
||||||
|
className="max-w-[2.5rem] border-none bg-mineshaft-500"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} className="px-0.5" />
|
<FontAwesomeIcon icon={faXmark} className="px-1" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
)}
|
||||||
)}
|
</ProjectPermissionCan>
|
||||||
</ProjectPermissionCan>
|
|
||||||
|
<Tooltip content="View details">
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="delete"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="star"
|
||||||
|
className="max-w-[2.5rem] border-none bg-mineshaft-500"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faEllipsis} className="px-1" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -42,7 +42,9 @@ export const UserAuditLogsSection = withPermission(
|
|||||||
<LogsSection
|
<LogsSection
|
||||||
showFilters={showFilter}
|
showFilters={showFilter}
|
||||||
filterClassName="bg-mineshaft-900 static"
|
filterClassName="bg-mineshaft-900 static"
|
||||||
presetActor={orgMembership.user.id}
|
presets={{
|
||||||
|
actorId: orgMembership.user.id
|
||||||
|
}}
|
||||||
isOrgAuditLogs
|
isOrgAuditLogs
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,29 @@
|
|||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Control, Controller, UseFormReset } from "react-hook-form";
|
import { Control, Controller, UseFormReset, UseFormWatch } from "react-hook-form";
|
||||||
import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faCheckCircle,
|
||||||
|
faChevronDown,
|
||||||
|
faFilterCircleXmark
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { Button, DatePicker, FormControl, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DatePicker,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
SelectItem
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
||||||
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
||||||
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
||||||
import { Actor } from "@app/hooks/api/auditLogs/types";
|
import { Actor } from "@app/hooks/api/auditLogs/types";
|
||||||
|
|
||||||
import { AuditLogFilterFormData } from "./types";
|
import { AuditLogFilterFormData } from "./types";
|
||||||
@ -20,13 +35,17 @@ const userAgentTypes = Object.entries(userAgentTTypeoNameMap).map(([value, label
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
presetActor?: string;
|
presets?: {
|
||||||
|
actorId?: string;
|
||||||
|
eventType?: EventType[];
|
||||||
|
};
|
||||||
className?: string;
|
className?: string;
|
||||||
control: Control<AuditLogFilterFormData>;
|
control: Control<AuditLogFilterFormData>;
|
||||||
reset: UseFormReset<AuditLogFilterFormData>;
|
reset: UseFormReset<AuditLogFilterFormData>;
|
||||||
|
watch: UseFormWatch<AuditLogFilterFormData>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LogsFilter = ({ presetActor, className, control, reset }: Props) => {
|
export const LogsFilter = ({ presets, className, control, reset, watch }: Props) => {
|
||||||
const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false);
|
const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false);
|
||||||
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
||||||
|
|
||||||
@ -71,6 +90,8 @@ export const LogsFilter = ({ presetActor, className, control, reset }: Props) =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectedEventTypes = watch("eventType") as EventType[] | undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
@ -82,29 +103,69 @@ export const LogsFilter = ({ presetActor, className, control, reset }: Props) =>
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="eventType"
|
name="eventType"
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
render={({ field }) => (
|
||||||
<FormControl
|
<FormControl label="Events">
|
||||||
label="Event"
|
<DropdownMenu>
|
||||||
errorText={error?.message}
|
<DropdownMenuTrigger asChild>
|
||||||
isError={Boolean(error)}
|
<div className="inline-flex w-full cursor-pointer items-center justify-between rounded-md border border-mineshaft-500 bg-mineshaft-700 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-mineshaft-200">
|
||||||
className="w-40"
|
{selectedEventTypes?.length === 1
|
||||||
>
|
? eventTypes.find((eventType) => eventType.value === selectedEventTypes[0])
|
||||||
<Select
|
?.label
|
||||||
{...(field.value ? { value: field.value } : { placeholder: "Select" })}
|
: selectedEventTypes?.length === 0
|
||||||
{...field}
|
? "Select event types"
|
||||||
onValueChange={(e) => onChange(e)}
|
: `${selectedEventTypes?.length} events selected`}
|
||||||
className="w-full border border-mineshaft-500 bg-mineshaft-700 text-mineshaft-100"
|
<FontAwesomeIcon icon={faChevronDown} className="ml-2 text-xs" />
|
||||||
>
|
</div>
|
||||||
{eventTypes.map(({ label, value }) => (
|
</DropdownMenuTrigger>
|
||||||
<SelectItem value={String(value || "")} key={label}>
|
<DropdownMenuContent align="start" className="z-[100] max-h-80 overflow-hidden">
|
||||||
{label}
|
<div className="max-h-80 overflow-y-auto">
|
||||||
</SelectItem>
|
{eventTypes && eventTypes.length > 0 ? (
|
||||||
))}
|
eventTypes.map((eventType) => {
|
||||||
</Select>
|
const isSelected = selectedEventTypes?.includes(
|
||||||
|
eventType.value as EventType
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={(event) => eventTypes.length > 1 && event.preventDefault()}
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedEventTypes?.includes(eventType.value as EventType)) {
|
||||||
|
field.onChange(
|
||||||
|
selectedEventTypes?.filter((e: string) => e !== eventType.value)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
field.onChange([...(selectedEventTypes || []), eventType.value]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
key={`event-type-${eventType.value}`}
|
||||||
|
icon={
|
||||||
|
isSelected ? (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCheckCircle}
|
||||||
|
className="pr-0.5 text-primary"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="pl-[1.01rem]" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
iconPos="left"
|
||||||
|
className="w-[28.4rem] text-sm"
|
||||||
|
>
|
||||||
|
{eventType.label}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{!isLoading && data && data.length > 0 && !presetActor && (
|
|
||||||
|
{!isLoading && data && data.length > 0 && !presets?.actorId && (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="actor"
|
name="actor"
|
||||||
@ -207,8 +268,8 @@ export const LogsFilter = ({ presetActor, className, control, reset }: Props) =>
|
|||||||
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} />}
|
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
reset({
|
reset({
|
||||||
eventType: undefined,
|
eventType: presets?.eventType || [],
|
||||||
actor: presetActor,
|
actor: presets?.actorId,
|
||||||
userAgentType: undefined,
|
userAgentType: undefined,
|
||||||
startDate: undefined,
|
startDate: undefined,
|
||||||
endDate: undefined
|
endDate: undefined
|
||||||
|
@ -5,24 +5,38 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|||||||
|
|
||||||
import { UpgradePlanModal } from "@app/components/v2";
|
import { UpgradePlanModal } from "@app/components/v2";
|
||||||
import { useSubscription } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType, EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { LogsFilter } from "./LogsFilter";
|
import { LogsFilter } from "./LogsFilter";
|
||||||
import { LogsTable } from "./LogsTable";
|
import { LogsTable, TAuditLogTableHeader } from "./LogsTable";
|
||||||
import { AuditLogFilterFormData, auditLogFilterFormSchema } from "./types";
|
import { AuditLogFilterFormData, auditLogFilterFormSchema } from "./types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
presetActor?: string;
|
presets?: {
|
||||||
|
actorId?: string;
|
||||||
|
eventType?: EventType[];
|
||||||
|
actorType?: ActorType;
|
||||||
|
startDate?: Date;
|
||||||
|
endDate?: Date;
|
||||||
|
eventMetadata?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
showFilters?: boolean;
|
showFilters?: boolean;
|
||||||
filterClassName?: string;
|
filterClassName?: string;
|
||||||
isOrgAuditLogs?: boolean;
|
isOrgAuditLogs?: boolean;
|
||||||
|
showActorColumn?: boolean;
|
||||||
|
remappedHeaders?: Partial<Record<TAuditLogTableHeader, string>>;
|
||||||
|
refetchInterval?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LogsSection = ({
|
export const LogsSection = ({
|
||||||
presetActor,
|
presets,
|
||||||
filterClassName,
|
filterClassName,
|
||||||
|
remappedHeaders,
|
||||||
isOrgAuditLogs,
|
isOrgAuditLogs,
|
||||||
|
showActorColumn,
|
||||||
|
refetchInterval,
|
||||||
showFilters
|
showFilters
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
@ -33,11 +47,12 @@ export const LogsSection = ({
|
|||||||
const { control, reset, watch } = useForm<AuditLogFilterFormData>({
|
const { control, reset, watch } = useForm<AuditLogFilterFormData>({
|
||||||
resolver: yupResolver(auditLogFilterFormSchema),
|
resolver: yupResolver(auditLogFilterFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
actor: presetActor,
|
actor: presets?.actorId,
|
||||||
|
eventType: presets?.eventType || [],
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
startDate: new Date(new Date().setDate(new Date().getDate() - 1)), // day before today
|
startDate: presets?.startDate ?? new Date(new Date().setDate(new Date().getDate() - 1)), // day before today
|
||||||
endDate: new Date(new Date(Date.now()).setHours(23, 59, 59, 999)) // end of today
|
endDate: presets?.endDate ?? new Date(new Date(Date.now()).setHours(23, 59, 59, 999)) // end of today
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,7 +62,7 @@ export const LogsSection = ({
|
|||||||
}
|
}
|
||||||
}, [subscription]);
|
}, [subscription]);
|
||||||
|
|
||||||
const eventType = watch("eventType") as EventType | undefined;
|
const eventType = watch("eventType") as EventType[] | undefined;
|
||||||
const userAgentType = watch("userAgentType") as UserAgentType | undefined;
|
const userAgentType = watch("userAgentType") as UserAgentType | undefined;
|
||||||
const actor = watch("actor");
|
const actor = watch("actor");
|
||||||
|
|
||||||
@ -59,19 +74,27 @@ export const LogsSection = ({
|
|||||||
{showFilters && (
|
{showFilters && (
|
||||||
<LogsFilter
|
<LogsFilter
|
||||||
className={filterClassName}
|
className={filterClassName}
|
||||||
presetActor={presetActor}
|
presets={presets}
|
||||||
control={control}
|
control={control}
|
||||||
|
watch={watch}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<LogsTable
|
<LogsTable
|
||||||
|
refetchInterval={refetchInterval}
|
||||||
|
remappedHeaders={remappedHeaders}
|
||||||
isOrgAuditLogs={isOrgAuditLogs}
|
isOrgAuditLogs={isOrgAuditLogs}
|
||||||
eventType={eventType}
|
showActorColumn={!!showActorColumn && !isOrgAuditLogs}
|
||||||
userAgentType={userAgentType}
|
filter={{
|
||||||
showActorColumn={!presetActor}
|
eventMetadata: presets?.eventMetadata,
|
||||||
actor={actor}
|
actorType: presets?.actorType,
|
||||||
startDate={startDate}
|
limit: 15,
|
||||||
endDate={endDate}
|
eventType,
|
||||||
|
userAgentType,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
actorId: actor
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<UpgradePlanModal
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
|
@ -15,43 +15,41 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useGetAuditLogs } from "@app/hooks/api";
|
import { useGetAuditLogs } from "@app/hooks/api";
|
||||||
import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
import { TGetAuditLogsFilter } from "@app/hooks/api/auditLogs/types";
|
||||||
|
|
||||||
import { LogsTableRow } from "./LogsTableRow";
|
import { LogsTableRow } from "./LogsTableRow";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
eventType?: EventType;
|
|
||||||
userAgentType?: UserAgentType;
|
|
||||||
actor?: string;
|
|
||||||
startDate?: Date;
|
|
||||||
endDate?: Date;
|
|
||||||
isOrgAuditLogs?: boolean;
|
isOrgAuditLogs?: boolean;
|
||||||
showActorColumn: boolean;
|
showActorColumn: boolean;
|
||||||
|
filter?: TGetAuditLogsFilter;
|
||||||
|
remappedHeaders?: Partial<Record<TAuditLogTableHeader, string>>;
|
||||||
|
refetchInterval?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUDIT_LOG_LIMIT = 15;
|
const AUDIT_LOG_LIMIT = 15;
|
||||||
|
|
||||||
|
const TABLE_HEADERS = ["Timestamp", "Event", "Project", "Actor", "Source", "Metadata"] as const;
|
||||||
|
export type TAuditLogTableHeader = (typeof TABLE_HEADERS)[number];
|
||||||
|
|
||||||
export const LogsTable = ({
|
export const LogsTable = ({
|
||||||
eventType,
|
|
||||||
userAgentType,
|
|
||||||
showActorColumn,
|
showActorColumn,
|
||||||
actor,
|
isOrgAuditLogs,
|
||||||
startDate,
|
filter,
|
||||||
endDate,
|
remappedHeaders,
|
||||||
isOrgAuditLogs
|
refetchInterval
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs(
|
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs(
|
||||||
{
|
{
|
||||||
eventType,
|
...filter,
|
||||||
userAgentType,
|
|
||||||
actor,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
limit: AUDIT_LOG_LIMIT
|
limit: AUDIT_LOG_LIMIT
|
||||||
},
|
},
|
||||||
!isOrgAuditLogs ? currentWorkspace?.id ?? "" : null
|
!isOrgAuditLogs ? currentWorkspace?.id ?? "" : null,
|
||||||
|
{
|
||||||
|
refetchInterval
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isEmpty = !isLoading && !data?.pages?.[0].length;
|
const isEmpty = !isLoading && !data?.pages?.[0].length;
|
||||||
@ -62,18 +60,24 @@ export const LogsTable = ({
|
|||||||
<Table>
|
<Table>
|
||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Timestamp</Th>
|
{TABLE_HEADERS.map((header, idx) => {
|
||||||
<Th>Event</Th>
|
if (
|
||||||
{isOrgAuditLogs && <Th>Project</Th>}
|
(header === "Project" && !isOrgAuditLogs) ||
|
||||||
{showActorColumn && <Th>Actor</Th>}
|
(header === "Actor" && !showActorColumn)
|
||||||
<Th>Source</Th>
|
) {
|
||||||
<Th>Metadata</Th>
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Th key={`table-header-${idx + 1}`}>{remappedHeaders?.[header] || header}</Th>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
data?.pages?.map((group, i) => (
|
data?.pages?.map((group, i) => (
|
||||||
<Fragment key={`auditlog-item-${i + 1}`}>
|
<Fragment key={`audit-log-fragment-${i + 1}`}>
|
||||||
{group.map((auditLog) => (
|
{group.map((auditLog) => (
|
||||||
<LogsTableRow
|
<LogsTableRow
|
||||||
showActorColumn={showActorColumn}
|
showActorColumn={showActorColumn}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Td, Tr } from "@app/components/v2";
|
import { Badge, Td, Tooltip, Tr } from "@app/components/v2";
|
||||||
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
||||||
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
||||||
import { Actor, AuditLog, Event } from "@app/hooks/api/auditLogs/types";
|
import { Actor, AuditLog, Event } from "@app/hooks/api/auditLogs/types";
|
||||||
@ -461,6 +461,21 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop
|
|||||||
<p>{`Secret Request Channels: ${event.metadata.secretRequestChannels}`}</p>
|
<p>{`Secret Request Channels: ${event.metadata.secretRequestChannels}`}</p>
|
||||||
</Td>
|
</Td>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case EventType.INTEGRATION_SYNCED:
|
||||||
|
return (
|
||||||
|
<Td>
|
||||||
|
<Tooltip
|
||||||
|
className="max-w-xs whitespace-normal break-words"
|
||||||
|
content={event.metadata.syncMessage!}
|
||||||
|
isDisabled={!event.metadata.syncMessage}
|
||||||
|
>
|
||||||
|
<Badge variant={event.metadata.isSynced ? "success" : "danger"}>
|
||||||
|
<p className="text-center">{event.metadata.isSynced ? "Successful" : "Failed"}</p>
|
||||||
|
</Badge>
|
||||||
|
</Tooltip>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <Td />;
|
return <Td />;
|
||||||
}
|
}
|
||||||
@ -484,16 +499,41 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop
|
|||||||
return formattedDate;
|
return formattedDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderSource = () => {
|
||||||
|
const { event, actor } = auditLog;
|
||||||
|
|
||||||
|
if (event.type === EventType.INTEGRATION_SYNCED) {
|
||||||
|
if (actor.type === ActorType.USER) {
|
||||||
|
return (
|
||||||
|
<Td>
|
||||||
|
<p>Manually triggered by {actor.metadata.email}</p>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platform / automatic syncs
|
||||||
|
return (
|
||||||
|
<Td>
|
||||||
|
<p>Automatically synced by Infisical</p>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Td>
|
||||||
|
<p>{userAgentTTypeoNameMap[auditLog.userAgentType]}</p>
|
||||||
|
<p>{auditLog.ipAddress}</p>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr className={`log-${auditLog.id} h-10 border-x-0 border-b border-t-0`}>
|
<Tr className={`log-${auditLog.id} h-10 border-x-0 border-b border-t-0`}>
|
||||||
<Td>{formatDate(auditLog.createdAt)}</Td>
|
<Td>{formatDate(auditLog.createdAt)}</Td>
|
||||||
<Td>{`${eventToNameMap[auditLog.event.type]}`}</Td>
|
<Td>{`${eventToNameMap[auditLog.event.type]}`}</Td>
|
||||||
{isOrgAuditLogs && <Td>{auditLog.project.name}</Td>}
|
{isOrgAuditLogs && <Td>{auditLog.project.name}</Td>}
|
||||||
{showActorColumn && renderActor(auditLog.actor)}
|
{showActorColumn && renderActor(auditLog.actor)}
|
||||||
<Td>
|
{renderSource()}
|
||||||
<p>{userAgentTTypeoNameMap[auditLog.userAgentType]}</p>
|
|
||||||
<p>{auditLog.ipAddress}</p>
|
|
||||||
</Td>
|
|
||||||
{renderMetadata(auditLog.event)}
|
{renderMetadata(auditLog.event)}
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,8 @@ import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
|||||||
|
|
||||||
export const auditLogFilterFormSchema = yup
|
export const auditLogFilterFormSchema = yup
|
||||||
.object({
|
.object({
|
||||||
eventType: yup.string().oneOf(Object.values(EventType), "Invalid event type"),
|
eventMetadata: yup.object({}).optional(),
|
||||||
|
eventType: yup.array(yup.string().oneOf(Object.values(EventType), "Invalid event type")),
|
||||||
actor: yup.string(),
|
actor: yup.string(),
|
||||||
userAgentType: yup.string().oneOf(Object.values(UserAgentType), "Invalid user agent type"),
|
userAgentType: yup.string().oneOf(Object.values(UserAgentType), "Invalid user agent type"),
|
||||||
startDate: yup.date(),
|
startDate: yup.date(),
|
||||||
|
@ -5,6 +5,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input } from "@app/components/v2";
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
|
import { useToggle } from "@app/hooks";
|
||||||
import { useGetAdminSlackConfig, useUpdateServerConfig } from "@app/hooks/api";
|
import { useGetAdminSlackConfig, useUpdateServerConfig } from "@app/hooks/api";
|
||||||
|
|
||||||
const slackFormSchema = z.object({
|
const slackFormSchema = z.object({
|
||||||
@ -65,6 +66,8 @@ export const IntegrationPanel = () => {
|
|||||||
|
|
||||||
const { data: adminSlackConfig } = useGetAdminSlackConfig();
|
const { data: adminSlackConfig } = useGetAdminSlackConfig();
|
||||||
const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig();
|
const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig();
|
||||||
|
const [isSlackClientIdFocused, setIsSlackClientIdFocused] = useToggle();
|
||||||
|
const [isSlackClientSecretFocused, setIsSlackClientSecretFocused] = useToggle();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (adminSlackConfig) {
|
if (adminSlackConfig) {
|
||||||
@ -120,6 +123,9 @@ export const IntegrationPanel = () => {
|
|||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
value={field.value || ""}
|
value={field.value || ""}
|
||||||
|
type={isSlackClientIdFocused ? "text" : "password"}
|
||||||
|
onFocus={() => setIsSlackClientIdFocused.on()}
|
||||||
|
onBlur={() => setIsSlackClientIdFocused.off()}
|
||||||
onChange={(e) => field.onChange(e.target.value)}
|
onChange={(e) => field.onChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -138,6 +144,9 @@ export const IntegrationPanel = () => {
|
|||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
value={field.value || ""}
|
value={field.value || ""}
|
||||||
|
type={isSlackClientSecretFocused ? "text" : "password"}
|
||||||
|
onFocus={() => setIsSlackClientSecretFocused.on()}
|
||||||
|
onBlur={() => setIsSlackClientSecretFocused.off()}
|
||||||
onChange={(e) => field.onChange(e.target.value)}
|
onChange={(e) => field.onChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
Reference in New Issue
Block a user