From 065807a0bcbe07f8baa267938a5500984c3ed89c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Oct 2022 15:43:57 +0200 Subject: [PATCH 001/133] fix: secret errors --- .../routes/api/v1/applications/handlers.ts | 4 + .../routes/applications/[id]/_Secret.svelte | 12 +- .../routes/applications/[id]/secrets.svelte | 15 +- pnpm-lock.yaml | 198 +++++++++++++----- 4 files changed, 166 insertions(+), 63 deletions(-) diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index d179aabfa..2076c8b5b 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -970,6 +970,10 @@ export async function saveSecret(request: FastifyRequest, reply: Fas try { const { id } = request.params const { name, value, isBuildSecret = false } = request.body + const found = await prisma.secret.findMany({ where: { applicationId: id, name } }) + if (found.length > 0) { + throw ({ message: 'Secret already exists.' }) + } await prisma.secret.create({ data: { name, value: encrypt(value.trim()), isBuildSecret, isPRMRSecret: false, application: { connect: { id } } } }); diff --git a/apps/ui/src/routes/applications/[id]/_Secret.svelte b/apps/ui/src/routes/applications/[id]/_Secret.svelte index d746d6f89..911ea0ad3 100644 --- a/apps/ui/src/routes/applications/[id]/_Secret.svelte +++ b/apps/ui/src/routes/applications/[id]/_Secret.svelte @@ -39,11 +39,11 @@ async function addNewSecret() { try { - if (!name) return errorNotification({ message: 'Name is required.' }); - if (!value) return errorNotification({ message: 'Value is required.' }); + if (!name.trim()) return errorNotification({ message: 'Name is required.' }); + if (!value.trim()) return errorNotification({ message: 'Value is required.' }); await post(`/applications/${id}/secrets`, { - name, - value, + name: name.trim(), + value: value.trim(), isBuildSecret }); cleanupState(); @@ -64,8 +64,8 @@ if (isNewSecret) return; try { await put(`/applications/${id}/secrets`, { - name, - value, + name: name.trim(), + value: value.trim(), isBuildSecret: changeIsBuildSecret ? isBuildSecret : undefined }); addToast({ diff --git a/apps/ui/src/routes/applications/[id]/secrets.svelte b/apps/ui/src/routes/applications/[id]/secrets.svelte index ba647d340..32bc9b535 100644 --- a/apps/ui/src/routes/applications/[id]/secrets.svelte +++ b/apps/ui/src/routes/applications/[id]/secrets.svelte @@ -48,10 +48,10 @@ .map((secret) => { const [name, ...rest] = secret.split('='); const value = rest.join('='); - const cleanValue = value?.replaceAll('"', '') || ''; + const cleanValue = (value?.replaceAll('"', '') || '').trim(); return { - name, - value: cleanValue, + name: name.trim(), + value: cleanValue.trim(), createSecret: !secrets.find((secret: any) => name === secret.name) }; }); @@ -60,6 +60,7 @@ batchSecretsPairs.map(({ name, value, createSecret }) => limit(async () => { try { + if (!name || !value) return; if (createSecret) { await post(`/applications/${id}/secrets`, { name, @@ -87,10 +88,10 @@ ); batchSecrets = ''; await refreshSecrets(); - addToast({ - message: 'Secrets saved.', - type: 'success' - }); + // addToast({ + // message: 'Secrets saved.', + // type: 'success' + // }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bac8b5276..933af8f24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ importers: '@iarna/toml': 2.2.5 '@ladjs/graceful': 3.0.2 '@prisma/client': 4.4.0 - '@types/node': 18.7.23 + '@types/node': 18.8.5 '@types/node-os-utils': 1.3.0 '@typescript-eslint/eslint-plugin': 5.38.1 '@typescript-eslint/parser': 5.38.1 @@ -34,21 +34,21 @@ importers: bree: 9.1.2 cabin: 9.1.2 compare-versions: 5.0.1 - csv-parse: ^5.3.0 - csvtojson: ^2.0.10 + csv-parse: 5.3.1 + csvtojson: 2.0.10 cuid: 2.1.8 dayjs: 1.11.5 dockerode: 3.3.4 dotenv-extended: 2.9.0 esbuild: 0.15.10 - eslint: 8.23.0 + eslint: 8.25.0 eslint-config-prettier: 8.5.0 eslint-plugin-prettier: 4.2.1 execa: 6.1.0 - fastify: 4.7.0 + fastify: 4.8.1 fastify-plugin: 4.2.1 generate-password: 1.7.0 - got: 12.5.1 + got: 12.5.2 is-ip: 5.0.0 is-port-reachable: 4.0.0 js-yaml: 4.1.0 @@ -69,7 +69,7 @@ importers: typescript: 4.8.4 unique-names-generator: 4.7.1 dependencies: - '@breejs/ts-worker': 2.0.0_z4zw7clouxa3aqbp4be65jzeva + '@breejs/ts-worker': 2.0.0_zrahc6pp4cqv4ttiqfvtjkibhy '@fastify/autoload': 5.4.0 '@fastify/cookie': 8.3.0 '@fastify/cors': 8.1.0 @@ -85,17 +85,17 @@ importers: bree: 9.1.2 cabin: 9.1.2 compare-versions: 5.0.1 - csv-parse: 5.3.0 + csv-parse: 5.3.1 csvtojson: 2.0.10 cuid: 2.1.8 dayjs: 1.11.5 dockerode: 3.3.4 dotenv-extended: 2.9.0 execa: 6.1.0 - fastify: 4.7.0 + fastify: 4.8.1 fastify-plugin: 4.2.1 generate-password: 1.7.0 - got: 12.5.1 + got: 12.5.2 is-ip: 5.0.0 is-port-reachable: 4.0.0 js-yaml: 4.1.0 @@ -111,14 +111,14 @@ importers: strip-ansi: 7.0.1 unique-names-generator: 4.7.1 devDependencies: - '@types/node': 18.7.23 + '@types/node': 18.8.5 '@types/node-os-utils': 1.3.0 - '@typescript-eslint/eslint-plugin': 5.38.1_pvy74qhkkcd5sngj4qereqoplm - '@typescript-eslint/parser': 5.38.1_nwctkcr5uyxf47tw7zkgamxmfq + '@typescript-eslint/eslint-plugin': 5.38.1_444tv3vqp6lpktlxqzfovoqee4 + '@typescript-eslint/parser': 5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q esbuild: 0.15.10 - eslint: 8.23.0 - eslint-config-prettier: 8.5.0_eslint@8.23.0 - eslint-plugin-prettier: 4.2.1_tgumt6uwl2md3n6uqnggd6wvce + eslint: 8.25.0 + eslint-config-prettier: 8.5.0_eslint@8.25.0 + eslint-plugin-prettier: 4.2.1_hvbqyfstm4urdpm6ffpwfka4e4 nodemon: 2.0.20 prettier: 2.7.1 rimraf: 3.0.2 @@ -253,7 +253,7 @@ packages: engines: {node: '>= 10'} dev: false - /@breejs/ts-worker/2.0.0_z4zw7clouxa3aqbp4be65jzeva: + /@breejs/ts-worker/2.0.0_zrahc6pp4cqv4ttiqfvtjkibhy: resolution: {integrity: sha512-6anHRcmgYlF7mrm/YVRn6rx2cegLuiY3VBxkkimOTWC/dVQeH336imVSuIKEGKTwiuNTPr2hswVdDSneNuXg3A==} engines: {node: '>= 12.11'} peerDependencies: @@ -261,7 +261,7 @@ packages: tsconfig-paths: '>= 4' dependencies: bree: 9.1.2 - ts-node: 10.8.2_gbhfbbeqrol7fxixnzbkbuanxe + ts-node: 10.8.2_ptpocrdt7oaz4ni5mlvucph5pa tsconfig-paths: 4.1.0 transitivePeerDependencies: - '@swc/core' @@ -312,6 +312,23 @@ packages: - supports-color dev: true + /@eslint/eslintrc/1.3.3: + resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.4.0 + globals: 13.15.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /@fastify/accept-negotiator/1.0.0: resolution: {integrity: sha512-4R/N2KfYeld7A5LGkai+iUFMahXcxxYbDp+XS2B1yuL3cdmZLJ9TlCnNzT3q5xFTqsYm0GPpinLUwfSwjcVjyA==} engines: {node: '>=14'} @@ -431,6 +448,17 @@ packages: - supports-color dev: true + /@humanwhocodes/config-array/0.10.7: + resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/gitignore-to-minimatch/1.0.2: resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} dev: true @@ -505,7 +533,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.7.23 + '@types/node': 18.8.5 playwright-core: 1.25.1 dev: true @@ -688,8 +716,8 @@ packages: resolution: {integrity: sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==} dev: false - /@types/node/18.7.23: - resolution: {integrity: sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==} + /@types/node/18.8.5: + resolution: {integrity: sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==} /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -708,7 +736,7 @@ packages: /@types/sass/1.43.1: resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} dependencies: - '@types/node': 18.7.23 + '@types/node': 18.8.5 dev: true /@typescript-eslint/eslint-plugin/5.36.1_lbwfnm54o3pmr3ypeqp3btnera: @@ -738,7 +766,7 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin/5.38.1_pvy74qhkkcd5sngj4qereqoplm: + /@typescript-eslint/eslint-plugin/5.38.1_444tv3vqp6lpktlxqzfovoqee4: resolution: {integrity: sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -749,12 +777,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.38.1_nwctkcr5uyxf47tw7zkgamxmfq + '@typescript-eslint/parser': 5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q '@typescript-eslint/scope-manager': 5.38.1 - '@typescript-eslint/type-utils': 5.38.1_nwctkcr5uyxf47tw7zkgamxmfq - '@typescript-eslint/utils': 5.38.1_nwctkcr5uyxf47tw7zkgamxmfq + '@typescript-eslint/type-utils': 5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q + '@typescript-eslint/utils': 5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q debug: 4.3.4 - eslint: 8.23.0 + eslint: 8.25.0 ignore: 5.2.0 regexpp: 3.2.0 semver: 7.3.7 @@ -784,7 +812,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.38.1_nwctkcr5uyxf47tw7zkgamxmfq: + /@typescript-eslint/parser/5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q: resolution: {integrity: sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -798,7 +826,7 @@ packages: '@typescript-eslint/types': 5.38.1 '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 debug: 4.3.4 - eslint: 8.23.0 + eslint: 8.25.0 typescript: 4.8.4 transitivePeerDependencies: - supports-color @@ -840,7 +868,7 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils/5.38.1_nwctkcr5uyxf47tw7zkgamxmfq: + /@typescript-eslint/type-utils/5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q: resolution: {integrity: sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -851,9 +879,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 - '@typescript-eslint/utils': 5.38.1_nwctkcr5uyxf47tw7zkgamxmfq + '@typescript-eslint/utils': 5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q debug: 4.3.4 - eslint: 8.23.0 + eslint: 8.25.0 tsutils: 3.21.0_typescript@4.8.4 typescript: 4.8.4 transitivePeerDependencies: @@ -930,7 +958,7 @@ packages: - typescript dev: true - /@typescript-eslint/utils/5.38.1_nwctkcr5uyxf47tw7zkgamxmfq: + /@typescript-eslint/utils/5.38.1_z4bbprzjrhnsfa24uvmcbu7f5q: resolution: {integrity: sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -940,9 +968,9 @@ packages: '@typescript-eslint/scope-manager': 5.38.1 '@typescript-eslint/types': 5.38.1 '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 - eslint: 8.23.0 + eslint: 8.25.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.23.0 + eslint-utils: 3.0.0_eslint@8.25.0 transitivePeerDependencies: - supports-color - typescript @@ -2359,8 +2387,8 @@ packages: engines: {node: '>=4'} hasBin: true - /csv-parse/5.3.0: - resolution: {integrity: sha512-UXJCGwvJ2fep39purtAn27OUYmxB1JQto+zhZ4QlJpzsirtSFbzLvip1aIgziqNdZp/TptvsKEV5BZSxe10/DQ==} + /csv-parse/5.3.1: + resolution: {integrity: sha512-R4Hv6eGJNzgcKdThZ6XORbSQ873HVcNke74QIq+LbwpT90LaZ8Xzl7KKiuIP16xq/P7ofzRt0h7S0xm+fVScsw==} dev: false /csvtojson/2.0.10: @@ -2954,7 +2982,16 @@ packages: eslint: 8.23.0 dev: true - /eslint-plugin-prettier/4.2.1_tgumt6uwl2md3n6uqnggd6wvce: + /eslint-config-prettier/8.5.0_eslint@8.25.0: + resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.25.0 + dev: true + + /eslint-plugin-prettier/4.2.1_hvbqyfstm4urdpm6ffpwfka4e4: resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -2965,8 +3002,8 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.23.0 - eslint-config-prettier: 8.5.0_eslint@8.23.0 + eslint: 8.25.0 + eslint-config-prettier: 8.5.0_eslint@8.25.0 prettier: 2.7.1 prettier-linter-helpers: 1.0.0 dev: true @@ -3007,6 +3044,16 @@ packages: eslint-visitor-keys: 2.1.0 dev: true + /eslint-utils/3.0.0_eslint@8.25.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.25.0 + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-visitor-keys/2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} @@ -3065,6 +3112,53 @@ packages: - supports-color dev: true + /eslint/8.25.0: + resolution: {integrity: sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.3.3 + '@humanwhocodes/config-array': 0.10.7 + '@humanwhocodes/module-importer': 1.0.1 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.25.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.0 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.15.0 + globby: 11.1.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-sdsl: 4.1.5 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /espree/9.4.0: resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3224,15 +3318,15 @@ packages: resolution: {integrity: sha512-dlGKiwLzRBKkEf5J5ho0uAD/Jdv8GQVUbriB3tAX3ehRUXE4gTV3lRd5inEg9li1aLzb0EGj8y2K4/8g1TN06g==} dev: false - /fastify/4.7.0: - resolution: {integrity: sha512-FK3WT6arZOd2Fm696vIn90DOFr1f8QZbFcvUzYRPJTBV0pzm1xN8Y3n9yegtv1ajAxpdTwuLhB10Wwb/ptMRqQ==} + /fastify/4.8.1: + resolution: {integrity: sha512-3an6yyXg7j5XPHTRxgFqUwIRaJPR7F8OPR8Uboo0SIgHx1orOqc9Y8Sl5Kz8+xK7JCy3c3mfRujskDjMu/nFfA==} dependencies: '@fastify/ajv-compiler': 3.3.1 '@fastify/error': 3.0.0 '@fastify/fast-json-stringify-compiler': 4.1.0 abstract-logging: 2.0.1 avvio: 8.2.0 - find-my-way: 7.2.0 + find-my-way: 7.3.1 light-my-request: 5.6.1 pino: 8.6.1 process-warning: 2.0.0 @@ -3295,8 +3389,8 @@ packages: dependencies: to-regex-range: 5.0.1 - /find-my-way/7.2.0: - resolution: {integrity: sha512-27SFA5sSYDYFZCQ/7SSJB0yhStTP/qxKP1OEC8feZvkHFRuD3fGcQ97Y+0w8HpKTDfMYWXGU3h2ETRGt5zPWyA==} + /find-my-way/7.3.1: + resolution: {integrity: sha512-kGvM08SOkqvheLcuQ8GW9t/H901Qb9rZEbcNWbXopzy4jDRoaJpJoObPSKf4MnQLZ20ZTp7rL5MpF6rf+pqmyg==} engines: {node: '>=14'} dependencies: fast-deep-equal: 3.1.3 @@ -3590,8 +3684,8 @@ packages: responselike: 2.0.0 dev: false - /got/12.5.1: - resolution: {integrity: sha512-sD16AK8cCyUoPtKr/NMvLTFFa+T3i3S+zoiuvhq0HP2YiqBZA9AtlBjAdsQBsLBK7slPuvmfE0OxhGi7N5dD4w==} + /got/12.5.2: + resolution: {integrity: sha512-guHGMSEcsA5m1oPRweXUJnug0vuvlkX9wx5hzOka+ZBrBUOJHU0Z1JcNu3QE5IPGnA5aXUsQHdWOD4eJg9/v3A==} engines: {node: '>=14.16'} dependencies: '@sindresorhus/is': 5.3.0 @@ -4031,6 +4125,10 @@ packages: engines: {node: '>=12'} dev: false + /js-sdsl/4.1.5: + resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} + dev: true + /js-tokens/3.0.2: resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} dev: true @@ -5118,7 +5216,7 @@ packages: dependencies: aggregate-error: 4.0.1 dns-socket: 4.2.2 - got: 12.5.1 + got: 12.5.2 is-ip: 4.0.0 dev: false @@ -6106,7 +6204,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ts-node/10.8.2_gbhfbbeqrol7fxixnzbkbuanxe: + /ts-node/10.8.2_ptpocrdt7oaz4ni5mlvucph5pa: resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} hasBin: true peerDependencies: @@ -6125,7 +6223,7 @@ packages: '@tsconfig/node12': 1.0.9 '@tsconfig/node14': 1.0.1 '@tsconfig/node16': 1.0.2 - '@types/node': 18.7.23 + '@types/node': 18.8.5 acorn: 8.8.0 acorn-walk: 8.2.0 arg: 4.1.3 From 410a78b36603fdf21fbf53ca0d58ab8f2d6b9223 Mon Sep 17 00:00:00 2001 From: Jordyn Date: Fri, 14 Oct 2022 10:49:53 +1100 Subject: [PATCH 002/133] fix: expose port is readonly on the wrong condition --- apps/ui/src/routes/applications/[id]/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ui/src/routes/applications/[id]/index.svelte b/apps/ui/src/routes/applications/[id]/index.svelte index acfe3c686..448a586ea 100644 --- a/apps/ui/src/routes/applications/[id]/index.svelte +++ b/apps/ui/src/routes/applications/[id]/index.svelte @@ -838,8 +838,8 @@ > Date: Fri, 14 Oct 2022 09:45:45 +0200 Subject: [PATCH 003/133] remove nix file --- csb.nix | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 csb.nix diff --git a/csb.nix b/csb.nix deleted file mode 100644 index bee4a5062..000000000 --- a/csb.nix +++ /dev/null @@ -1,10 +0,0 @@ -with import {}; - -stdenv.mkDerivation { - name = "environment"; - buildInputs = [ - git - git-lfs - docker-compose - ]; -} \ No newline at end of file From 462eea90c0d61cdda4ec4411003be3efd73e88ac Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 14 Oct 2022 15:48:37 +0200 Subject: [PATCH 004/133] tons of updates --- CONTRIBUTION.md | 6 +- apps/api/src/lib/common.ts | 6 +- apps/api/src/lib/services/handlers.ts | 207 ++++---- apps/api/src/lib/templates.ts | 175 +++++++ .../src/routes/api/v1/services/handlers.ts | 73 ++- .../lib/components/CopyPasswordField.svelte | 6 + apps/ui/src/lib/components/DocLink.svelte | 37 +- .../ui/src/lib/components/ExternalLink.svelte | 10 + .../src/lib/components/ServiceStatus.svelte | 31 ++ .../components/Services/Appwrite.svelte} | 26 +- .../components/Services/Fider.svelte} | 46 +- .../src/lib/components/Services/Ghost.svelte | 101 ++++ .../components/Services/GlitchTip.svelte} | 56 +-- .../components/Services/Hasura.svelte} | 25 +- .../components/Services/MeiliSearch.svelte} | 6 +- .../components/Services/MinIO.svelte} | 12 +- .../components/Services/Moodle.svelte} | 24 +- .../Services/PlausibleAnalytics.svelte} | 53 ++- .../components/Services/Searxng.svelte} | 12 +- .../components/Services/Taiga.svelte} | 34 +- .../components/Services/Umami.svelte} | 10 +- .../components/Services/VSCodeServer.svelte} | 6 +- .../components/Services/Weblate.svelte} | 26 +- .../components/Services/Wordpress.svelte} | 34 +- apps/ui/src/lib/components/Services/index.ts | 15 + apps/ui/src/lib/components/Services/utils.ts | 15 + .../components/svg/services/Appwrite.svelte | 2 +- .../lib/components/svg/services/Fider.svelte | 2 +- .../svg/services/ServiceIcons.svelte | 45 +- .../src/lib/components/svg/services/index.ts | 18 +- apps/ui/src/lib/store.ts | 4 +- .../src/routes/applications/[id]/_Menu.svelte | 2 +- .../routes/applications/[id]/_Secret.svelte | 4 +- .../routes/applications/[id]/_Storage.svelte | 3 +- .../routes/applications/[id]/__layout.svelte | 2 +- .../[id]/configuration/source.svelte | 2 +- .../routes/applications/[id]/danger.svelte | 2 +- .../src/routes/applications/[id]/index.svelte | 2 +- .../applications/[id]/logs/index.svelte | 7 +- .../routes/applications/[id]/secrets.svelte | 4 - apps/ui/src/routes/services/[id]/_Menu.svelte | 136 ++++++ .../src/routes/services/[id]/_Secret.svelte | 14 +- .../routes/services/[id]/_ServiceLinks.svelte | 119 ++--- .../services/[id]/_Services/_Ghost.svelte | 99 ---- .../services/[id]/_Services/_Services.svelte | 450 ------------------ .../src/routes/services/[id]/_Storage.svelte | 85 ++-- .../src/routes/services/[id]/__layout.svelte | 383 ++++++--------- .../[id]/configuration/destination.svelte | 2 +- .../services/[id]/configuration/type.svelte | 39 +- .../ui/src/routes/services/[id]/danger.svelte | 66 +++ apps/ui/src/routes/services/[id]/index.svelte | 436 +++++++++++++++-- .../routes/services/[id]/logs/index.svelte | 121 +++-- .../src/routes/services/[id]/secrets.svelte | 37 +- .../src/routes/services/[id]/storages.svelte | 49 +- 54 files changed, 1760 insertions(+), 1427 deletions(-) create mode 100644 apps/api/src/lib/templates.ts create mode 100644 apps/ui/src/lib/components/ExternalLink.svelte create mode 100644 apps/ui/src/lib/components/ServiceStatus.svelte rename apps/ui/src/{routes/services/[id]/_Services/_Appwrite.svelte => lib/components/Services/Appwrite.svelte} (74%) rename apps/ui/src/{routes/services/[id]/_Services/_Fider.svelte => lib/components/Services/Fider.svelte} (77%) create mode 100644 apps/ui/src/lib/components/Services/Ghost.svelte rename apps/ui/src/{routes/services/[id]/_Services/_GlitchTip.svelte => lib/components/Services/GlitchTip.svelte} (78%) rename apps/ui/src/{routes/services/[id]/_Services/_Hasura.svelte => lib/components/Services/Hasura.svelte} (61%) rename apps/ui/src/{routes/services/[id]/_Services/_MeiliSearch.svelte => lib/components/Services/MeiliSearch.svelte} (68%) rename apps/ui/src/{routes/services/[id]/_Services/_MinIO.svelte => lib/components/Services/MinIO.svelte} (77%) rename apps/ui/src/{routes/services/[id]/_Services/_Moodle.svelte => lib/components/Services/Moodle.svelte} (80%) rename apps/ui/src/{routes/services/[id]/_Services/_PlausibleAnalytics.svelte => lib/components/Services/PlausibleAnalytics.svelte} (64%) rename apps/ui/src/{routes/services/[id]/_Services/_Searxng.svelte => lib/components/Services/Searxng.svelte} (63%) rename apps/ui/src/{routes/services/[id]/_Services/_Taiga.svelte => lib/components/Services/Taiga.svelte} (71%) rename apps/ui/src/{routes/services/[id]/_Services/_Umami.svelte => lib/components/Services/Umami.svelte} (76%) rename apps/ui/src/{routes/services/[id]/_Services/_VSCodeServer.svelte => lib/components/Services/VSCodeServer.svelte} (68%) rename apps/ui/src/{routes/services/[id]/_Services/_Weblate.svelte => lib/components/Services/Weblate.svelte} (59%) rename apps/ui/src/{routes/services/[id]/_Services/_Wordpress.svelte => lib/components/Services/Wordpress.svelte} (87%) create mode 100644 apps/ui/src/lib/components/Services/index.ts create mode 100644 apps/ui/src/lib/components/Services/utils.ts create mode 100644 apps/ui/src/routes/services/[id]/_Menu.svelte delete mode 100644 apps/ui/src/routes/services/[id]/_Services/_Ghost.svelte delete mode 100644 apps/ui/src/routes/services/[id]/_Services/_Services.svelte create mode 100644 apps/ui/src/routes/services/[id]/danger.svelte diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 9f4bf9321..52c6d91d4 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -111,13 +111,11 @@ At [here](apps/api/src/lib/services/serviceFields.ts) add details about the inpu > SVG is recommended, but you can use PNG as well. It should have the `isAbsolute` variable with the suitable CSS classes, primarily for sizing and positioning. -- At [here](apps/ui/src/lib/components/svg/services/ServiceIcons.svelte) include the new logo with `isAbsolute` property. - - At [here](apps/ui/src/routes/services/[id]/_ServiceLinks.svelte) add links to the documentation of the service. ### Custom fields on the UI -By default the URL and name are shown on the UI. Everything else needs to be added [here](apps/ui/src/routes/services/[id]/_Services/_Services.svelte) +By default the URL and name are shown on the UI. Everything else needs to be added [here](apps/ui/src/lib/components/Services) -> If you need to show more details on the frontend, such as users/passwords, you need to add Svelte component [here](apps/ui/src/routes/services/[id]/_Services) with an underscore. For example, see other [here](apps/ui/src/routes/services/[id]/_Services/_Umami.svelte). +> If you need to show more details on the frontend, such as users/passwords, you need to add Svelte component [here](apps/ui/src/lib/components/Services). For example, see other [here](apps/ui/src/lib/components/Services/Umami.svelte). Good job! 👏 \ No newline at end of file diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 75e75947f..32ed080dc 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -198,7 +198,7 @@ export const encrypt = (text: string) => { if (text) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); - const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); + const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]); return JSON.stringify({ iv: iv.toString('hex'), content: encrypted.toString('hex') @@ -1681,7 +1681,9 @@ export function persistentVolumes(id, persistentStorage, config) { for (const [key, value] of Object.entries(config)) { if (value.volumes) { for (const volume of value.volumes) { - volumeSet.add(volume); + if (!volume.startsWith('/var/run/docker.sock')) { + volumeSet.add(volume); + } } } } diff --git a/apps/api/src/lib/services/handlers.ts b/apps/api/src/lib/services/handlers.ts index bcaf07d36..408da0000 100644 --- a/apps/api/src/lib/services/handlers.ts +++ b/apps/api/src/lib/services/handlers.ts @@ -6,82 +6,83 @@ import { ServiceStartStop } from '../../routes/api/v1/services/types'; import { asyncSleep, ComposeFile, createDirectories, defaultComposeConfiguration, errorHandler, executeDockerCmd, getDomain, getFreePublicPort, getServiceFromDB, getServiceImage, getServiceMainPort, isARM, isDev, makeLabelForServices, persistentVolumes, prisma } from '../common'; import { defaultServiceConfigurations } from '../services'; import { OnlyId } from '../../types'; +import templates from '../templates' -export async function startService(request: FastifyRequest) { - try { - const { type } = request.params - if (type === 'plausibleanalytics') { - return await startPlausibleAnalyticsService(request) - } - if (type === 'nocodb') { - return await startNocodbService(request) - } - if (type === 'minio') { - return await startMinioService(request) - } - if (type === 'vscodeserver') { - return await startVscodeService(request) - } - if (type === 'wordpress') { - return await startWordpressService(request) - } - if (type === 'vaultwarden') { - return await startVaultwardenService(request) - } - if (type === 'languagetool') { - return await startLanguageToolService(request) - } - if (type === 'n8n') { - return await startN8nService(request) - } - if (type === 'uptimekuma') { - return await startUptimekumaService(request) - } - if (type === 'ghost') { - return await startGhostService(request) - } - if (type === 'meilisearch') { - return await startMeilisearchService(request) - } - if (type === 'umami') { - return await startUmamiService(request) - } - if (type === 'hasura') { - return await startHasuraService(request) - } - if (type === 'fider') { - return await startFiderService(request) - } - if (type === 'moodle') { - return await startMoodleService(request) - } - if (type === 'appwrite') { - return await startAppWriteService(request) - } - if (type === 'glitchTip') { - return await startGlitchTipService(request) - } - if (type === 'searxng') { - return await startSearXNGService(request) - } - if (type === 'weblate') { - return await startWeblateService(request) - } - if (type === 'taiga') { - return await startTaigaService(request) - } - if (type === 'grafana') { - return await startGrafanaService(request) - } - if (type === 'trilium') { - return await startTriliumService(request) - } +// export async function startService(request: FastifyRequest) { +// try { +// const { type } = request.params +// if (type === 'plausibleanalytics') { +// return await startPlausibleAnalyticsService(request) +// } +// if (type === 'nocodb') { +// return await startNocodbService(request) +// } +// if (type === 'minio') { +// return await startMinioService(request) +// } +// if (type === 'vscodeserver') { +// return await startVscodeService(request) +// } +// if (type === 'wordpress') { +// return await startWordpressService(request) +// } +// if (type === 'vaultwarden') { +// return await startVaultwardenService(request) +// } +// if (type === 'languagetool') { +// return await startLanguageToolService(request) +// } +// if (type === 'n8n') { +// return await startN8nService(request) +// } +// if (type === 'uptimekuma') { +// return await startUptimekumaService(request) +// } +// if (type === 'ghost') { +// return await startGhostService(request) +// } +// if (type === 'meilisearch') { +// return await startMeilisearchService(request) +// } +// if (type === 'umami') { +// return await startUmamiService(request) +// } +// if (type === 'hasura') { +// return await startHasuraService(request) +// } +// if (type === 'fider') { +// return await startFiderService(request) +// } +// if (type === 'moodle') { +// return await startMoodleService(request) +// } +// if (type === 'appwrite') { +// return await startAppWriteService(request) +// } +// if (type === 'glitchTip') { +// return await startGlitchTipService(request) +// } +// if (type === 'searxng') { +// return await startSearXNGService(request) +// } +// if (type === 'weblate') { +// return await startWeblateService(request) +// } +// if (type === 'taiga') { +// return await startTaigaService(request) +// } +// if (type === 'grafana') { +// return await startGrafanaService(request) +// } +// if (type === 'trilium') { +// return await startTriliumService(request) +// } - throw `Service type ${type} not supported.` - } catch (error) { - throw { status: 500, message: error?.message || error } - } -} +// throw `Service type ${type} not supported.` +// } catch (error) { +// throw { status: 500, message: error?.message || error } +// } +// } export async function stopService(request: FastifyRequest) { try { return await stopServiceContainers(request) @@ -684,54 +685,54 @@ async function startLanguageToolService(request: FastifyRequest) { +export async function startService(request: FastifyRequest) { try { const { id } = request.params; const teamId = request.user.teamId; + const service = await getServiceFromDB({ id, teamId }); const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort, persistentStorage } = service; + + let template = templates.find((template) => template.name === type); + + template = JSON.parse(JSON.stringify(template).replaceAll('$$id', id).replaceAll('$$fqdn', service.fqdn)) + const network = destinationDockerId && destinationDocker.network; - const port = getServiceMainPort('n8n'); - const { workdir } = await createDirectories({ repository: type, buildId: id }); - const image = getServiceImage(type); - - const config = { - n8n: { - image: `${image}:${version}`, - volumes: [`${id}-n8n:/root/.n8n`], - environmentVariables: { - WEBHOOK_URL: `${service.fqdn}` - } + const config = {}; + for (const service in template.services) { + config[service] = { + container_name: id, + image: template.services[service].image.replace('$$core_version', version), + expose: template.services[service].ports, + // ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), + volumes: template.services[service].volumes, + environment: {}, + depends_on: template.services[service].depends_on, + ulimits: template.services[service].ulimits, + labels: makeLabelForServices(type), + ...defaultComposeConfiguration(network), + } + if (serviceSecret.length > 0) { + serviceSecret.forEach((secret) => { + config[service].environment[secret.name] = secret.value; + }); } - }; - if (serviceSecret.length > 0) { - serviceSecret.forEach((secret) => { - config.n8n.environmentVariables[secret.name] = secret.value; - }); } + const { workdir } = await createDirectories({ repository: type, buildId: id }); const { volumeMounts } = persistentVolumes(id, persistentStorage, config) + const composeFile: ComposeFile = { version: '3.8', - services: { - [id]: { - container_name: id, - image: config.n8n.image, - volumes: config.n8n.volumes, - environment: config.n8n.environmentVariables, - labels: makeLabelForServices('n8n'), - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(network), - } - }, + services: config, networks: { [network]: { external: true } }, volumes: volumeMounts - }; + } const composeFileDestination = `${workdir}/docker-compose.yaml`; await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); await startServiceContainers(destinationDocker.id, composeFileDestination) diff --git a/apps/api/src/lib/templates.ts b/apps/api/src/lib/templates.ts new file mode 100644 index 000000000..2155fcb24 --- /dev/null +++ b/apps/api/src/lib/templates.ts @@ -0,0 +1,175 @@ +export default [ + { + "templateVersion": "1.0.0", + "serviceDefaultVersion": "0.198.1", + "name": "n8n", + "displayName": "n8n.io", + "isOfficial": true, + "description": "n8n is a free and open node based Workflow Automation Tool.", + "services": { + "$$id": { + "documentation": "Taken from https://hub.docker.com/r/n8nio/n8n", + "depends_on": [], + "image": "n8nio/n8n:$$core_version", + "volumes": [ + "$$id-data:/root/.n8n", + "$$id-data-write:/files", + "/var/run/docker.sock:/var/run/docker.sock" + ], + "environment": [ + "WEBHOOK_URL=$$fqdn" + ], + "ports": [ + "5678" + ] + } + }, + "variables": [] + }, + { + "templateVersion": "1.0.0", + "serviceDefaultVersion": "stable", + "name": "plausibleanalytics", + "displayName": "PlausibleAnalytics", + "isOfficial": true, + "description": "Plausible is a lightweight and open-source website analytics tool.", + "services": { + "$$id": { + "documentation": "Taken from https://plausible.io/", + "command": ['sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"'], + "depends_on": [ + "$$id-postgresql", + "$$id-clickhouse" + ], + "image": "plausible/analytics:$$core_version", + "environment": [ + "ADMIN_USER_EMAIL=$$secret_email", + "ADMIN_USER_NAME=$$secret_name", + "ADMIN_USER_PASSWORD=$$secret_password", + "BASE_URL=$$fqdn", + "SECRET_KEY_BASE=$$secret_key_base", + "DISABLE_AUTH=$$secret_disable_auth", + "DISABLE_REGISTRATION=$$secret_disable_registration", + "DATABASE_URL=postgresql://$$secret_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$secret_postgresql_database", + "CLICKHOUSE_DATABASE_URL=http://$$id-clickhouse:8123/plausible", + ], + "ports": [ + "8000" + ], + }, + "$$id-postgresql": { + "documentation": "Taken from https://plausible.io/", + "image": "bitnami/postgresql:13.2.0", + "environment": [ + "POSTGRESQL_PASSWORD=$$secret_postgresql_password", + "POSTGRESQL_USERNAME=$$secret_postgresql_username", + "POSTGRESQL_DATABASE=$$secret_postgresql_database", + ], + + }, + "$$id-clickhouse": { + "documentation": "Taken from https://plausible.io/", + "build": "$$workdir", + "image": "yandex/clickhouse-server:21.3.2.5", + "ulimits": { + "nofile": { + "soft": 262144, + "hard": 262144 + } + }, + "extras": { + "files:": [ + { + location: '$$workdir/clickhouse-config.xml', + content: 'warningtrue' + }, + { + location: '$$workdir/clickhouse-user-config.xml', + content: '00' + }, + { + location: '$$workdir/init.query', + content: 'CREATE DATABASE IF NOT EXISTS plausible;' + }, + { + location: '$$workdir/init-db.sh', + content: 'clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query' + } + ] + } + }, + + }, + "variables": [ + { + "id": "$$secret_email", + "label": "Admin Email", + "defaultValue": "admin@example.com", + "description": "This is the admin email. Please change it.", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_name", + "label": "Admin Name", + "defaultValue": "admin", + "description": "This is the admin username. Please change it.", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_password", + "label": "Admin Password", + "description": "This is the admin password. Please change it.", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_secret_key_base", + "label": "Secret Key Base", + "description": "", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_disable_auth", + "label": "Disable Auth", + "defaultValue": "false", + "description": "", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_disable_registration", + "label": "Disable Registration", + "defaultValue": "true", + "description": "", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_disable_registration", + "label": "Disable Registration", + "defaultValue": "true", + "description": "", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_postgresql_username", + "label": "PostgreSQL Username", + "defaultValue": "postgresql", + "description": "", + "validRegex": /^([^\s^\/])+$/ + }, + { + "id": "$$secret_postgresql_password", + "label": "PostgreSQL Password", + "defaultValue": "postgresql", + "description": "", + "validRegex": /^([^\s^\/])+$/ + } + , + { + "id": "$$secret_postgresql_database", + "label": "PostgreSQL Database", + "defaultValue": "plausible", + "description": "", + "validRegex": /^([^\s^\/])+$/ + } + ] + } +] diff --git a/apps/api/src/routes/api/v1/services/handlers.ts b/apps/api/src/routes/api/v1/services/handlers.ts index 2dcc434b6..cb7e2796f 100644 --- a/apps/api/src/routes/api/v1/services/handlers.ts +++ b/apps/api/src/routes/api/v1/services/handlers.ts @@ -5,6 +5,7 @@ import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage import { day } from '../../../../lib/dayjs'; import { checkContainer, isContainerExited } from '../../../../lib/docker'; import cuid from 'cuid'; +import templates from '../../../../lib/templates'; import type { OnlyId } from '../../../../types'; import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetGlitchTipSettings, SetWordpressSettings } from './types'; @@ -73,25 +74,53 @@ export async function getServiceStatus(request: FastifyRequest) { let isRestarting = false; const service = await getServiceFromDB({ id, teamId }); const { destinationDockerId, settings } = service; - + let payload = {} if (destinationDockerId) { - const status = await checkContainer({ dockerId: service.destinationDocker.id, container: id }); - if (status?.found) { - isRunning = status.status.isRunning; - isExited = status.status.isExited; - isRestarting = status.status.isRestarting + const { stdout: containers } = await executeDockerCmd({ + dockerId: service.destinationDocker.id, + command: + `docker ps -a --filter "label=com.docker.compose.project=${id}" --format '{{json .}}'` + }); + const containersArray = containers.trim().split('\n'); + if (containersArray.length > 0 && containersArray[0] !== '') { + for (const container of containersArray) { + let isRunning = false; + let isExited = false; + let isRestarting = false; + const containerObj = JSON.parse(container); + const status = containerObj.State + if (status === 'running') { + isRunning = true; + } + if (status === 'exited') { + isExited = true; + } + if (status === 'restarting') { + isRestarting = true; + } + payload[containerObj.Names] = { + status: { + isRunning, + isExited, + isRestarting + } + + } + } } } - return { - isRunning, - isExited, - settings - } + return payload } catch ({ status, message }) { return errorHandler({ status, message }) } } +function parseAndFindServiceTemplates(service: any) { + const foundTemplate = templates.find(t => t.name === service.type) + if (foundTemplate) { + return JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', service.id).replaceAll('$$fqdn', service.fqdn)) + } +} export async function getService(request: FastifyRequest) { try { const teamId = request.user.teamId; @@ -102,7 +131,8 @@ export async function getService(request: FastifyRequest) { } return { settings: await listSettings(), - service + service, + template: parseAndFindServiceTemplates(service) } } catch ({ status, message }) { return errorHandler({ status, message }) @@ -111,7 +141,7 @@ export async function getService(request: FastifyRequest) { export async function getServiceType(request: FastifyRequest) { try { return { - types: supportedServiceTypesAndVersions + services: templates } } catch ({ status, message }) { return errorHandler({ status, message }) @@ -120,8 +150,21 @@ export async function getServiceType(request: FastifyRequest) { export async function saveServiceType(request: FastifyRequest, reply: FastifyReply) { try { const { id } = request.params; - const { type } = request.body; - await configureServiceType({ id, type }); + const { name, variables = [], serviceDefaultVersion = 'latest' } = request.body; + if (variables.length > 0) { + for (const variable of variables) { + const { id: variableId, defaultValue, value = null } = variable; + if (variableId.startsWith('$$secret_')) { + const secretName = variableId.replace('$$secret_', ''); + let secretValue = defaultValue || value || null; + if (secretValue) secretValue = encrypt(secretValue); + await prisma.serviceSecret.create({ + data: { name: secretName, value: secretValue, service: { connect: { id } } } + }) + } + } + } + await prisma.service.update({ where: { id }, data: { type: name, version: serviceDefaultVersion } }) return reply.code(201).send() } catch ({ status, message }) { return errorHandler({ status, message }) diff --git a/apps/ui/src/lib/components/CopyPasswordField.svelte b/apps/ui/src/lib/components/CopyPasswordField.svelte index 9083fa47c..6aedb6ff5 100644 --- a/apps/ui/src/lib/components/CopyPasswordField.svelte +++ b/apps/ui/src/lib/components/CopyPasswordField.svelte @@ -38,6 +38,8 @@ class={disabledClass} class:pr-10={true} class:pr-20={value && isHttps} + class:border={required && !value} + class:border-red-500={required && !value} {placeholder} type="text" {id} @@ -54,6 +56,8 @@ type="text" class:pr-10={true} class:pr-20={value && isHttps} + class:border={required && !value} + class:border-red-500={required && !value} {id} {name} {required} @@ -70,6 +74,8 @@ class={disabledClass} class:pr-10={true} class:pr-20={value && isHttps} + class:border={required && !value} + class:border-red-500={required && !value} type="password" {id} {name} diff --git a/apps/ui/src/lib/components/DocLink.svelte b/apps/ui/src/lib/components/DocLink.svelte index d561566f0..1abb893f5 100644 --- a/apps/ui/src/lib/components/DocLink.svelte +++ b/apps/ui/src/lib/components/DocLink.svelte @@ -1,6 +1,9 @@ - - - - - + + + + + {text} + {#if isExternal} + + {/if} -See details in the documentation +{#if !text} + See details in the documentation +{/if} diff --git a/apps/ui/src/lib/components/ExternalLink.svelte b/apps/ui/src/lib/components/ExternalLink.svelte new file mode 100644 index 000000000..62f2e312a --- /dev/null +++ b/apps/ui/src/lib/components/ExternalLink.svelte @@ -0,0 +1,10 @@ + + + diff --git a/apps/ui/src/lib/components/ServiceStatus.svelte b/apps/ui/src/lib/components/ServiceStatus.svelte new file mode 100644 index 000000000..b14e6cdc3 --- /dev/null +++ b/apps/ui/src/lib/components/ServiceStatus.svelte @@ -0,0 +1,31 @@ + + +{#if serviceStatus.isRunning} + Running +{:else if serviceStatus.isStopped || serviceStatus.isExited} + Stopped +{:else if serviceStatus.isRestarting} + Restarting +{/if} diff --git a/apps/ui/src/routes/services/[id]/_Services/_Appwrite.svelte b/apps/ui/src/lib/components/Services/Appwrite.svelte similarity index 74% rename from apps/ui/src/routes/services/[id]/_Services/_Appwrite.svelte rename to apps/ui/src/lib/components/Services/Appwrite.svelte index 5af58ae7f..9b5f8bf6d 100644 --- a/apps/ui/src/routes/services/[id]/_Services/_Appwrite.svelte +++ b/apps/ui/src/lib/components/Services/Appwrite.svelte @@ -6,12 +6,12 @@ export let readOnly: any; -
-
Appwrite
+
+
Appwrite
-
-
+
+
-
+
-
-
MariaDB
+
+
MariaDB
-
-
+
+
-
+
-
+
-
+
-
+
-
-
Fider
+
+
Fider
-
-
+
+
-
+
-
-
Email
+
+
Email
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
PostgreSQL
+
+
PostgreSQL
-
-
+
+
-
+
-
+
+ import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; + import Explainer from '$lib/components/Explainer.svelte'; + import { t } from '$lib/translations'; + export let readOnly: any; + export let service: any; + + +
+
+ Ghost +
+
+
+
+ + +
+
+ + +
+
+
+
MariaDB
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
diff --git a/apps/ui/src/routes/services/[id]/_Services/_GlitchTip.svelte b/apps/ui/src/lib/components/Services/GlitchTip.svelte similarity index 78% rename from apps/ui/src/routes/services/[id]/_Services/_GlitchTip.svelte rename to apps/ui/src/lib/components/Services/GlitchTip.svelte index 2d824b484..c0591018b 100644 --- a/apps/ui/src/routes/services/[id]/_Services/_GlitchTip.svelte +++ b/apps/ui/src/lib/components/Services/GlitchTip.svelte @@ -49,11 +49,11 @@ } -
-
GlitchTip
+
+
GlitchTip
-
+
-
-
-
Email settings
+
+
Email Settings
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Default User & Superuser
-
+
-
+
-
+
-
-
PostgreSQL
+
+
PostgreSQL
-
-
+
+
-
+
-
+
-
-
Hasura
+
+
Hasura
-
+
-
Hasura Console is not enabled by default for security reasons.
To enable it, add the following secret:

HASURA_GRAPHQL_ENABLE_CONSOLE=true
- -
-
PostgreSQL
+
+ Hasura Console is not enabled by default for security reasons. +
To enable it, add the following secret:

+ HASURA_GRAPHQL_ENABLE_CONSOLE=true +
+
+
PostgreSQL
-
-
+
+
-
+
-
+
-
-
MeiliSearch
+
+
MeiliSearch
-
+
-
-
MinIO
+
+
MinIO
-
-
+
+
-
+
{#if !service.minio.apiFqdn} -
+
-
-
Moodle
+
+
Moodle
-
+
-
+
-
+
-
-
MariaDB
+
+
MariaDB
-
+
-
+
-
+
-
+
-
+
+ export let service: any; + export let readOnly: any; + import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import Explainer from '$lib/components/Explainer.svelte'; import { appSession, status } from '$lib/store'; import { t } from '$lib/translations'; - export let service: any; - export let readOnly: any; + import ServiceStatus from '../ServiceStatus.svelte'; + let serviceStatus = { + isExited: false, + isRunning: false, + isRestarting: false, + isStopped: false + }; + + $: if (Object.keys($status.service.statuses).length > 0) { + let { isExited, isRunning, isRestarting } = $status.service.statuses[service.id].status; + serviceStatus.isExited = isExited; + serviceStatus.isRunning = isRunning; + serviceStatus.isRestarting = isRestarting; + serviceStatus.isStopped = !isExited && !isRunning && !isRestarting; + } -
-
Plausible Analytics
+
+
Plausible Analytics
+
-
-
+
+
-
+
-
+
-
+
-
-
PostgreSQL
+ +
+
PostgreSQL
+
-
-
+
+
-
+
-
+
+
+
ClickHouse
+ +
\ No newline at end of file diff --git a/apps/ui/src/routes/services/[id]/_Services/_Searxng.svelte b/apps/ui/src/lib/components/Services/Searxng.svelte similarity index 63% rename from apps/ui/src/routes/services/[id]/_Services/_Searxng.svelte rename to apps/ui/src/lib/components/Services/Searxng.svelte index d726deb82..5e7d045ed 100644 --- a/apps/ui/src/routes/services/[id]/_Services/_Searxng.svelte +++ b/apps/ui/src/lib/components/Services/Searxng.svelte @@ -4,11 +4,11 @@ export let service: any; -
-
SearXNG
+
+
SearXNG
-
+
-
-
Redis
+
+
Redis
-
+
-
-
Taiga
+
+
Taiga
-
+
-
-
Django
+
+
Django
-
+
-
+
-
-
RabbitMQ
+
+
RabbitMQ
-
+
-
+
-
-
PostgreSQL
+
+
PostgreSQL
-
+
-
+
-
+
-
+
-
-
Umami
+
+
Umami
-
-
+
+
-
+