From 142b83cc1324acc42a51bc17c469bf819630176b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 19 Apr 2021 09:46:05 +0200 Subject: [PATCH] v1.0.7 (#32) New features: - Automatic error reporting (enabled by default) - Increase build times by leveraging docker build caches - Fixes: - Fix error handling - Fix vue autodetect - Custom dockerfile is not the default Others: - Cleanup `logs-servers` collection, because old errors are not standardized - New Traefik proxy version - Standardized directory configurations --- .gitignore | 1 - api/app.js | 2 +- api/buildPacks/custom/index.js | 22 +- api/buildPacks/helpers.js | 19 +- api/buildPacks/nodejs/index.js | 26 +- api/buildPacks/php/index.js | 18 +- api/buildPacks/rust/index.js | 38 +- api/buildPacks/static/index.js | 22 +- api/libs/applications/build/container.js | 25 +- api/libs/applications/cleanup/index.js | 42 +- api/libs/applications/configuration.js | 115 ++-- api/libs/applications/deploy/copyFiles.js | 2 +- api/libs/applications/deploy/deploy.js | 98 ++- .../applications/github/cloneRepository.js | 44 +- api/libs/applications/index.js | 40 +- api/libs/docker.js | 29 +- api/libs/http-error/handlers.js | 75 ++ api/libs/http-error/index.js | 58 ++ api/libs/http-error/interfaces.js | 6 + api/libs/http-error/utils.js | 31 + api/libs/http-error/validation.js | 239 +++++++ api/libs/logging.js | 51 +- api/models/Logs/Server.js | 9 +- api/models/Settings.js | 3 +- api/routes/v1/application/check.js | 9 +- api/routes/v1/application/deploy/index.js | 52 +- api/routes/v1/application/deploy/logs.js | 2 +- api/routes/v1/application/logs.js | 4 +- api/routes/v1/dashboard/index.js | 6 +- api/routes/v1/databases/index.js | 4 +- api/routes/v1/login/github.js | 10 +- api/routes/v1/server/index.js | 2 +- api/routes/v1/settings/index.js | 13 +- api/routes/v1/upgrade/index.js | 4 +- api/routes/v1/webhooks/deploy.js | 26 +- api/server.js | 43 +- install/Dockerfile-new | 25 +- install/coolify-template-dev.yml | 73 ++ install/coolify-template.yml | 2 +- package.json | 4 +- pnpm-lock.yaml | 649 ++++++++---------- .../Configuration/ActiveTab/General.svelte | 6 +- .../Configuration/Configuration.svelte | 2 +- .../Application/Configuration/Tabs.svelte | 22 +- src/index.css | 5 + src/pages/_layout.svelte | 60 +- .../[name]/[branch]/logs/[deployId].svelte | 9 +- src/pages/application/_layout.svelte | 9 +- src/pages/settings/index.svelte | 65 +- src/utils/templates.js | 2 +- vite.config.js | 4 +- 51 files changed, 1295 insertions(+), 832 deletions(-) create mode 100644 api/libs/http-error/handlers.js create mode 100644 api/libs/http-error/index.js create mode 100644 api/libs/http-error/interfaces.js create mode 100644 api/libs/http-error/utils.js create mode 100644 api/libs/http-error/validation.js create mode 100644 install/coolify-template-dev.yml diff --git a/.gitignore b/.gitignore index 7b5ba795e7..90e0552cc4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ dist-ssr yarn-error.log api/development/console.log .pnpm-debug.log -yarn.lock .pnpm-store \ No newline at end of file diff --git a/api/app.js b/api/app.js index eac63297c9..d1b42fd35a 100644 --- a/api/app.js +++ b/api/app.js @@ -1,7 +1,7 @@ module.exports = async function (fastify, opts) { // Private routes fastify.register(async function (server) { - if (process.env.NODE_ENV === 'production') server.register(require('./plugins/authentication')) + server.register(require('./plugins/authentication')) server.register(require('./routes/v1/upgrade'), { prefix: '/upgrade' }) server.register(require('./routes/v1/settings'), { prefix: '/settings' }) server.register(require('./routes/v1/dashboard'), { prefix: '/dashboard' }) diff --git a/api/buildPacks/custom/index.js b/api/buildPacks/custom/index.js index 79524da2ed..e572b9f7be 100644 --- a/api/buildPacks/custom/index.js +++ b/api/buildPacks/custom/index.js @@ -2,18 +2,14 @@ const fs = require('fs').promises const { streamEvents, docker } = require('../../libs/docker') module.exports = async function (configuration) { - try { - const path = `${configuration.general.workdir}/${configuration.build.directory ? configuration.build.directory : ''}` - if (fs.stat(`${path}/Dockerfile`)) { - const stream = await docker.engine.buildImage( - { src: ['.'], context: path }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } else { - throw { error: 'No custom dockerfile found.', type: 'app' } - } - } catch (error) { - throw { error, type: 'server' } + const path = `${configuration.general.workdir}/${configuration.build.directory ? configuration.build.directory : ''}` + if (fs.stat(`${path}/Dockerfile`)) { + const stream = await docker.engine.buildImage( + { src: ['.'], context: path }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) + } else { + throw new Error('No custom dockerfile found.') } } diff --git a/api/buildPacks/helpers.js b/api/buildPacks/helpers.js index 1d90af8573..0e87dff75c 100644 --- a/api/buildPacks/helpers.js +++ b/api/buildPacks/helpers.js @@ -4,22 +4,19 @@ const buildImageNodeDocker = (configuration) => { return [ 'FROM node:lts', 'WORKDIR /usr/src/app', - `COPY ${configuration.build.directory} ./`, + `COPY ${configuration.build.directory}/package*.json .`, configuration.build.command.installation && `RUN ${configuration.build.command.installation}`, + `COPY ./${configuration.build.directory} .`, `RUN ${configuration.build.command.build}` ].join('\n') } async function buildImage (configuration) { - try { - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, buildImageNodeDocker(configuration)) - const stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } catch (error) { - throw { error, type: 'server' } - } + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, buildImageNodeDocker(configuration)) + const stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) } module.exports = { diff --git a/api/buildPacks/nodejs/index.js b/api/buildPacks/nodejs/index.js index 7b199d3303..25b62b87f3 100644 --- a/api/buildPacks/nodejs/index.js +++ b/api/buildPacks/nodejs/index.js @@ -7,24 +7,22 @@ const publishNodejsDocker = (configuration) => { 'FROM node:lts', 'WORKDIR /usr/src/app', configuration.build.command.build - ? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./` - : `COPY ${configuration.build.directory} ./`, - configuration.build.command.installation && `RUN ${configuration.build.command.installation}`, + ? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} .` + : ` + COPY ${configuration.build.directory}/package*.json . + RUN ${configuration.build.command.installation} + COPY ./${configuration.build.directory} .`, `EXPOSE ${configuration.publish.port}`, 'CMD [ "yarn", "start" ]' ].join('\n') } module.exports = async function (configuration) { - try { - if (configuration.build.command.build) await buildImage(configuration) - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishNodejsDocker(configuration)) - const stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } catch (error) { - throw { error, type: 'server' } - } + if (configuration.build.command.build) await buildImage(configuration) + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishNodejsDocker(configuration)) + const stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) } diff --git a/api/buildPacks/php/index.js b/api/buildPacks/php/index.js index b96700ee2e..e22791d095 100644 --- a/api/buildPacks/php/index.js +++ b/api/buildPacks/php/index.js @@ -6,21 +6,17 @@ const publishPHPDocker = (configuration) => { 'FROM php:apache', 'RUN a2enmod rewrite', 'WORKDIR /usr/src/app', - `COPY .${configuration.build.directory} /var/www/html`, + `COPY ./${configuration.build.directory} /var/www/html`, 'EXPOSE 80', ' CMD ["apache2-foreground"]' ].join('\n') } module.exports = async function (configuration) { - try { - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishPHPDocker(configuration)) - const stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } catch (error) { - throw { error, type: 'server' } - } + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishPHPDocker(configuration)) + const stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) } diff --git a/api/buildPacks/rust/index.js b/api/buildPacks/rust/index.js index 6cbed387ac..e4b076810c 100644 --- a/api/buildPacks/rust/index.js +++ b/api/buildPacks/rust/index.js @@ -37,28 +37,24 @@ const cacheRustDocker = (configuration, custom) => { } module.exports = async function (configuration) { - try { - const cargoToml = await execShellAsync(`cat ${configuration.general.workdir}/Cargo.toml`) - const parsedToml = TOML.parse(cargoToml) - const custom = { - name: parsedToml.package.name - } - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, cacheRustDocker(configuration, custom)) + const cargoToml = await execShellAsync(`cat ${configuration.general.workdir}/Cargo.toml`) + const parsedToml = TOML.parse(cargoToml) + const custom = { + name: parsedToml.package.name + } + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, cacheRustDocker(configuration, custom)) - let stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:cache` } - ) - await streamEvents(stream, configuration) + let stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:cache` } + ) + await streamEvents(stream, configuration) - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishRustDocker(configuration, custom)) + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishRustDocker(configuration, custom)) - stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } catch (error) { - throw { error, type: 'server' } - } + stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) } diff --git a/api/buildPacks/static/index.js b/api/buildPacks/static/index.js index e6c4d56104..4fef433b27 100644 --- a/api/buildPacks/static/index.js +++ b/api/buildPacks/static/index.js @@ -9,24 +9,20 @@ const publishStaticDocker = (configuration) => { 'COPY nginx.conf /etc/nginx/nginx.conf', 'WORKDIR /usr/share/nginx/html', configuration.build.command.build - ? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./` - : `COPY ${configuration.build.directory} ./`, + ? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} .` + : `COPY ./${configuration.build.directory} .`, 'EXPOSE 80', 'CMD ["nginx", "-g", "daemon off;"]' ].join('\n') } module.exports = async function (configuration) { - try { - if (configuration.build.command.build) await buildImage(configuration) - await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration)) + if (configuration.build.command.build) await buildImage(configuration) + await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration)) - const stream = await docker.engine.buildImage( - { src: ['.'], context: configuration.general.workdir }, - { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } - ) - await streamEvents(stream, configuration) - } catch (error) { - throw { error, type: 'server' } - } + const stream = await docker.engine.buildImage( + { src: ['.'], context: configuration.general.workdir }, + { t: `${configuration.build.container.name}:${configuration.build.container.tag}` } + ) + await streamEvents(stream, configuration) } diff --git a/api/libs/applications/build/container.js b/api/libs/applications/build/container.js index a8209ec618..e22a47e337 100644 --- a/api/libs/applications/build/container.js +++ b/api/libs/applications/build/container.js @@ -9,22 +9,12 @@ module.exports = async function (configuration) { const execute = packs[configuration.build.pack] if (execute) { - try { - await Deployment.findOneAndUpdate( - { repoId: id, branch, deployId, organization, name, domain }, - { repoId: id, branch, deployId, organization, name, domain, progress: 'inprogress' }) - await saveAppLog('### Building application.', configuration) - - await execute(configuration) - - await saveAppLog('### Building done.', configuration) - } catch (error) { - await Deployment.findOneAndUpdate( - { repoId: id, branch, deployId, organization, name, domain }, - { repoId: id, branch, deployId, organization, name, domain, progress: 'failed' }) - if (error.stack) throw { error: error.stack, type: 'server' } - throw { error, type: 'app' } - } + await Deployment.findOneAndUpdate( + { repoId: id, branch, deployId, organization, name, domain }, + { repoId: id, branch, deployId, organization, name, domain, progress: 'inprogress' }) + await saveAppLog('### Building application.', configuration) + await execute(configuration) + await saveAppLog('### Building done.', configuration) } else { try { await Deployment.findOneAndUpdate( @@ -33,7 +23,6 @@ module.exports = async function (configuration) { } catch (error) { // Hmm. } - - throw { error: 'No buildpack found.', type: 'app' } + throw new Error('No buildpack found.') } } diff --git a/api/libs/applications/cleanup/index.js b/api/libs/applications/cleanup/index.js index c5c3b9f663..3cfa6269fb 100644 --- a/api/libs/applications/cleanup/index.js +++ b/api/libs/applications/cleanup/index.js @@ -3,39 +3,25 @@ const { execShellAsync } = require('../../common') const Deployment = require('../../../models/Deployment') async function purgeImagesContainers () { - try { - await execShellAsync('docker container prune -f') - await execShellAsync('docker image prune -f --filter=label!=coolify-reserve=true') - } catch (error) { - throw { error, type: 'server' } - } + await execShellAsync('docker container prune -f') + await execShellAsync('docker image prune -f --filter=label!=coolify-reserve=true') } -async function cleanupStuckedDeploymentsInDB (configuration) { - const { id } = configuration.repository - const deployId = configuration.general.deployId - try { - // Cleanup stucked deployments. - const deployments = await Deployment.find({ repoId: id, deployId: { $ne: deployId }, progress: { $in: ['queued', 'inprogress'] } }) - for (const deployment of deployments) { - await Deployment.findByIdAndUpdate(deployment._id, { $set: { progress: 'failed' } }) - } - } catch (error) { - throw { error, type: 'server' } - } +async function cleanupStuckedDeploymentsInDB () { + // Cleanup stucked deployments. + await Deployment.updateMany( + { progress: { $in: ['queued', 'inprogress'] } }, + { progress: 'failed' } + ) } async function deleteSameDeployments (configuration) { - try { - await (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application').map(async s => { - const running = JSON.parse(s.Spec.Labels.configuration) - if (running.repository.id === configuration.repository.id && running.repository.branch === configuration.repository.branch) { - await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`) - } - }) - } catch (error) { - throw { error, type: 'server' } - } + await (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application').map(async s => { + const running = JSON.parse(s.Spec.Labels.configuration) + if (running.repository.id === configuration.repository.id && running.repository.branch === configuration.repository.branch) { + await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`) + } + }) } module.exports = { cleanupStuckedDeploymentsInDB, deleteSameDeployments, purgeImagesContainers } diff --git a/api/libs/applications/configuration.js b/api/libs/applications/configuration.js index d8fce5c277..53b913feb4 100644 --- a/api/libs/applications/configuration.js +++ b/api/libs/applications/configuration.js @@ -9,69 +9,64 @@ function getUniq () { } function setDefaultConfiguration (configuration) { - try { - const nickname = getUniq() - const deployId = cuid() - - const shaBase = JSON.stringify({ repository: configuration.repository }) - const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex') - - const baseServiceConfiguration = { - replicas: 1, - restart_policy: { - condition: 'any', - max_attempts: 3 - }, - update_config: { - parallelism: 1, - delay: '10s', - order: 'start-first' - }, - rollback_config: { - parallelism: 1, - delay: '10s', - order: 'start-first', - failure_action: 'rollback' - } - } - - configuration.build.container.name = sha256.slice(0, 15) - - configuration.general.nickname = nickname - configuration.general.deployId = deployId - configuration.general.workdir = `/tmp/${deployId}` - - if (!configuration.publish.path) configuration.publish.path = '/' - if (!configuration.publish.port) { - if (configuration.build.pack === 'php') { - configuration.publish.port = 80 - } else if (configuration.build.pack === 'static') { - configuration.publish.port = 80 - } else if (configuration.build.pack === 'nodejs') { - configuration.publish.port = 3000 - } else if (configuration.build.pack === 'rust') { - configuration.publish.port = 3000 - } + const nickname = getUniq() + const deployId = cuid() + + const shaBase = JSON.stringify({ repository: configuration.repository }) + const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex') + + const baseServiceConfiguration = { + replicas: 1, + restart_policy: { + condition: 'any', + max_attempts: 3 + }, + update_config: { + parallelism: 1, + delay: '10s', + order: 'start-first' + }, + rollback_config: { + parallelism: 1, + delay: '10s', + order: 'start-first', + failure_action: 'rollback' } + } - if (!configuration.build.directory) { - configuration.build.directory = '/' - } - if (!configuration.publish.directory) { - configuration.publish.directory = '/' + configuration.build.container.name = sha256.slice(0, 15) + + configuration.general.nickname = nickname + configuration.general.deployId = deployId + configuration.general.workdir = `/tmp/${deployId}` + + if (!configuration.publish.path) configuration.publish.path = '/' + if (!configuration.publish.port) { + if (configuration.build.pack === 'php') { + configuration.publish.port = 80 + } else if (configuration.build.pack === 'static') { + configuration.publish.port = 80 + } else if (configuration.build.pack === 'nodejs') { + configuration.publish.port = 3000 + } else if (configuration.build.pack === 'rust') { + configuration.publish.port = 3000 } + } - if (configuration.build.pack === 'static' || configuration.build.pack === 'nodejs') { - if (!configuration.build.command.installation) configuration.build.command.installation = 'yarn install' - } + if (!configuration.build.directory) configuration.build.directory = '' + if (configuration.build.directory.startsWith('/')) configuration.build.directory = configuration.build.directory.replace('/', '') - configuration.build.container.baseSHA = crypto.createHash('sha256').update(JSON.stringify(baseServiceConfiguration)).digest('hex') - configuration.baseServiceConfiguration = baseServiceConfiguration + if (!configuration.publish.directory) configuration.publish.directory = '' + if (configuration.publish.directory.startsWith('/')) configuration.publish.directory = configuration.publish.directory.replace('/', '') - return configuration - } catch (error) { - throw { error, type: 'server' } + if (configuration.build.pack === 'static' || configuration.build.pack === 'nodejs') { + if (!configuration.build.command.installation) configuration.build.command.installation = 'yarn install' } + + configuration.build.container.baseSHA = crypto.createHash('sha256').update(JSON.stringify(baseServiceConfiguration)).digest('hex') + configuration.baseServiceConfiguration = baseServiceConfiguration + + return configuration } async function updateServiceLabels (configuration) { @@ -86,12 +81,8 @@ async function updateServiceLabels (configuration) { }) if (found) { const { ID } = found - try { - const Labels = { ...JSON.parse(found.Spec.Labels.configuration), ...configuration } - await execShellAsync(`docker service update --label-add configuration='${JSON.stringify(Labels)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${configuration.build.container.tag}' ${ID}`) - } catch (error) { - console.log(error) - } + const Labels = { ...JSON.parse(found.Spec.Labels.configuration), ...configuration } + await execShellAsync(`docker service update --label-add configuration='${JSON.stringify(Labels)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${configuration.build.container.tag}' ${ID}`) } } diff --git a/api/libs/applications/deploy/copyFiles.js b/api/libs/applications/deploy/copyFiles.js index bb6defeaa3..9d9c8212ab 100644 --- a/api/libs/applications/deploy/copyFiles.js +++ b/api/libs/applications/deploy/copyFiles.js @@ -59,6 +59,6 @@ module.exports = async function (configuration) { ) } } catch (error) { - throw { error, type: 'server' } + throw new Error(error) } } diff --git a/api/libs/applications/deploy/deploy.js b/api/libs/applications/deploy/deploy.js index 0528320867..5094709a08 100644 --- a/api/libs/applications/deploy/deploy.js +++ b/api/libs/applications/deploy/deploy.js @@ -6,77 +6,71 @@ const { saveAppLog } = require('../../logging') const { deleteSameDeployments } = require('../cleanup') module.exports = async function (configuration, imageChanged) { - try { - const generateEnvs = {} - for (const secret of configuration.publish.secrets) { - generateEnvs[secret.name] = secret.value - } - const containerName = configuration.build.container.name + const generateEnvs = {} + for (const secret of configuration.publish.secrets) { + generateEnvs[secret.name] = secret.value + } + const containerName = configuration.build.container.name - // Only save SHA256 of it in the configuration label - const baseServiceConfiguration = configuration.baseServiceConfiguration - delete configuration.baseServiceConfiguration + // Only save SHA256 of it in the configuration label + const baseServiceConfiguration = configuration.baseServiceConfiguration + delete configuration.baseServiceConfiguration - const stack = { - version: '3.8', - services: { - [containerName]: { - image: `${configuration.build.container.name}:${configuration.build.container.tag}`, - networks: [`${docker.network}`], - environment: generateEnvs, - deploy: { - ...baseServiceConfiguration, - labels: [ - 'managedBy=coolify', - 'type=application', - 'configuration=' + JSON.stringify(configuration), - 'traefik.enable=true', - 'traefik.http.services.' + + const stack = { + version: '3.8', + services: { + [containerName]: { + image: `${configuration.build.container.name}:${configuration.build.container.tag}`, + networks: [`${docker.network}`], + environment: generateEnvs, + deploy: { + ...baseServiceConfiguration, + labels: [ + 'managedBy=coolify', + 'type=application', + 'configuration=' + JSON.stringify(configuration), + 'traefik.enable=true', + 'traefik.http.services.' + configuration.build.container.name + `.loadbalancer.server.port=${configuration.publish.port}`, - 'traefik.http.routers.' + + 'traefik.http.routers.' + configuration.build.container.name + '.entrypoints=websecure', - 'traefik.http.routers.' + + 'traefik.http.routers.' + configuration.build.container.name + '.rule=Host(`' + configuration.publish.domain + '`) && PathPrefix(`' + configuration.publish.path + '`)', - 'traefik.http.routers.' + + 'traefik.http.routers.' + configuration.build.container.name + '.tls.certresolver=letsencrypt', - 'traefik.http.routers.' + + 'traefik.http.routers.' + configuration.build.container.name + '.middlewares=global-compress' - ] - } - } - }, - networks: { - [`${docker.network}`]: { - external: true + ] } } + }, + networks: { + [`${docker.network}`]: { + external: true + } } - await saveAppLog('### Publishing.', configuration) - await fs.writeFile(`${configuration.general.workdir}/stack.yml`, yaml.dump(stack)) - if (imageChanged) { - // console.log('image changed') - await execShellAsync(`docker service update --image ${configuration.build.container.name}:${configuration.build.container.tag} ${configuration.build.container.name}_${configuration.build.container.name}`) - } else { - // console.log('new deployment or force deployment or config changed') - await deleteSameDeployments(configuration) - await execShellAsync( + } + await saveAppLog('### Publishing.', configuration) + await fs.writeFile(`${configuration.general.workdir}/stack.yml`, yaml.dump(stack)) + if (imageChanged) { + // console.log('image changed') + await execShellAsync(`docker service update --image ${configuration.build.container.name}:${configuration.build.container.tag} ${configuration.build.container.name}_${configuration.build.container.name}`) + } else { + // console.log('new deployment or force deployment or config changed') + await deleteSameDeployments(configuration) + await execShellAsync( `cat ${configuration.general.workdir}/stack.yml | docker stack deploy --prune -c - ${containerName}` - ) - } - - await saveAppLog('### Published done!', configuration) - } catch (error) { - console.log(error) - await saveAppLog(`Error occured during deployment: ${error.message}`, configuration) - throw { error, type: 'server' } + ) } + + await saveAppLog('### Published done!', configuration) } diff --git a/api/libs/applications/github/cloneRepository.js b/api/libs/applications/github/cloneRepository.js index f710b4d353..c670ea592c 100644 --- a/api/libs/applications/github/cloneRepository.js +++ b/api/libs/applications/github/cloneRepository.js @@ -15,30 +15,24 @@ module.exports = async function (configuration) { iss: parseInt(github.app.id) } - try { - const jwtToken = jwt.sign(payload, githubPrivateKey, { - algorithm: 'RS256' - }) - const accessToken = await axios({ - method: 'POST', - url: `https://api.github.com/app/installations/${github.installation.id}/access_tokens`, - data: {}, - headers: { - Authorization: 'Bearer ' + jwtToken, - Accept: 'application/vnd.github.machine-man-preview+json' - } - }) - await execShellAsync( + const jwtToken = jwt.sign(payload, githubPrivateKey, { + algorithm: 'RS256' + }) + const accessToken = await axios({ + method: 'POST', + url: `https://api.github.com/app/installations/${github.installation.id}/access_tokens`, + data: {}, + headers: { + Authorization: 'Bearer ' + jwtToken, + Accept: 'application/vnd.github.machine-man-preview+json' + } + }) + await execShellAsync( `mkdir -p ${workdir} && git clone -q -b ${branch} https://x-access-token:${accessToken.data.token}@github.com/${organization}/${name}.git ${workdir}/` - ) - configuration.build.container.tag = ( - await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`) - ) - .replace('\n', '') - .slice(0, 7) - } catch (error) { - cleanupTmp(workdir) - if (error.stack) console.log(error.stack) - throw { error, type: 'server' } - } + ) + configuration.build.container.tag = ( + await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`) + ) + .replace('\n', '') + .slice(0, 7) } diff --git a/api/libs/applications/index.js b/api/libs/applications/index.js index 38c1b8498f..8754267391 100644 --- a/api/libs/applications/index.js +++ b/api/libs/applications/index.js @@ -1,6 +1,5 @@ const dayjs = require('dayjs') -const { saveServerLog } = require('../logging') const { cleanupTmp } = require('../common') const { saveAppLog } = require('../logging') @@ -8,37 +7,26 @@ const copyFiles = require('./deploy/copyFiles') const buildContainer = require('./build/container') const deploy = require('./deploy/deploy') const Deployment = require('../../models/Deployment') -const { cleanupStuckedDeploymentsInDB, purgeImagesContainers } = require('./cleanup') +const { purgeImagesContainers } = require('./cleanup') const { updateServiceLabels } = require('./configuration') async function queueAndBuild (configuration, imageChanged) { const { id, organization, name, branch } = configuration.repository const { domain } = configuration.publish const { deployId, nickname, workdir } = configuration.general - try { - await new Deployment({ - repoId: id, branch, deployId, domain, organization, name, nickname - }).save() - await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration) - await copyFiles(configuration) - await buildContainer(configuration) - await deploy(configuration, imageChanged) - await Deployment.findOneAndUpdate( - { repoId: id, branch, deployId, organization, name, domain }, - { repoId: id, branch, deployId, organization, name, domain, progress: 'done' }) - await updateServiceLabels(configuration) - cleanupTmp(workdir) - await purgeImagesContainers() - } catch (error) { - await cleanupStuckedDeploymentsInDB(configuration) - cleanupTmp(workdir) - const { type } = error.error - if (type === 'app') { - await saveAppLog(error.error, configuration, true) - } else { - await saveServerLog({ event: error.error, configuration }) - } - } + await new Deployment({ + repoId: id, branch, deployId, domain, organization, name, nickname + }).save() + await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration) + await copyFiles(configuration) + await buildContainer(configuration) + await deploy(configuration, imageChanged) + await Deployment.findOneAndUpdate( + { repoId: id, branch, deployId, organization, name, domain }, + { repoId: id, branch, deployId, organization, name, domain, progress: 'done' }) + await updateServiceLabels(configuration) + cleanupTmp(workdir) + await purgeImagesContainers() } module.exports = { queueAndBuild } diff --git a/api/libs/docker.js b/api/libs/docker.js index eb22e53258..1ef28ca5c4 100644 --- a/api/libs/docker.js +++ b/api/libs/docker.js @@ -8,24 +8,21 @@ const docker = { network: process.env.DOCKER_NETWORK } async function streamEvents (stream, configuration) { - try { - await new Promise((resolve, reject) => { - docker.engine.modem.followProgress(stream, onFinished, onProgress) - function onFinished (err, res) { - if (err) reject(err) - resolve(res) - } - function onProgress (event) { - if (event.error) { - reject(event.error) - return - } + await new Promise((resolve, reject) => { + docker.engine.modem.followProgress(stream, onFinished, onProgress) + function onFinished (err, res) { + if (err) reject(err) + resolve(res) + } + function onProgress (event) { + if (event.error) { + saveAppLog(event.error, configuration, true) + reject(event.error) + } else if (event.stream) { saveAppLog(event.stream, configuration) } - }) - } catch (error) { - throw { error, type: 'app' } - } + } + }) } module.exports = { streamEvents, docker } diff --git a/api/libs/http-error/handlers.js b/api/libs/http-error/handlers.js new file mode 100644 index 0000000000..013d6c783c --- /dev/null +++ b/api/libs/http-error/handlers.js @@ -0,0 +1,75 @@ +/* eslint-disable */ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handleErrors = exports.handleValidationError = exports.handleNotFoundError = void 0; +const http_errors_enhanced_1 = require("http-errors-enhanced"); +const interfaces_1 = require("./interfaces"); +const utils_1 = require("./utils"); +const validation_1 = require("./validation"); +function handleNotFoundError(request, reply) { + handleErrors(new http_errors_enhanced_1.NotFoundError('Not found.'), request, reply); +} +exports.handleNotFoundError = handleNotFoundError; +function handleValidationError(error, request) { + /* + As seen in https://github.com/fastify/fastify/blob/master/lib/validation.js + the error.message will always start with the relative section (params, querystring, headers, body) + and fastify throws on first failing section. + */ + const section = error.message.match(/^\w+/)[0]; + return new http_errors_enhanced_1.BadRequestError('One or more validations failed trying to process your request.', { + failedValidations: validation_1.convertValidationErrors(section, Reflect.get(request, section), error.validation) + }); +} +exports.handleValidationError = handleValidationError; +function handleErrors(error, request, reply) { + var _a, _b; + // It is a generic error, handle it + const code = error.code; + if (!('statusCode' in error)) { + if ('validation' in error && ((_a = request[interfaces_1.kHttpErrorsEnhancedConfiguration]) === null || _a === void 0 ? void 0 : _a.convertValidationErrors)) { + // If it is a validation error, convert errors to human friendly format + error = handleValidationError(error, request); + } + else if ((_b = request[interfaces_1.kHttpErrorsEnhancedConfiguration]) === null || _b === void 0 ? void 0 : _b.hideUnhandledErrors) { + // It is requested to hide the error, just log it and then create a generic one + request.log.error({ error: http_errors_enhanced_1.serializeError(error) }); + error = new http_errors_enhanced_1.InternalServerError('An error occurred trying to process your request.'); + } + else { + // Wrap in a HttpError, making the stack explicitily available + error = new http_errors_enhanced_1.InternalServerError(http_errors_enhanced_1.serializeError(error)); + Object.defineProperty(error, 'stack', { enumerable: true }); + } + } + else if (code === 'INVALID_CONTENT_TYPE' || code === 'FST_ERR_CTP_INVALID_MEDIA_TYPE') { + error = new http_errors_enhanced_1.UnsupportedMediaTypeError(utils_1.upperFirst(validation_1.validationMessagesFormatters.contentType())); + } + else if (code === 'FST_ERR_CTP_EMPTY_JSON_BODY') { + error = new http_errors_enhanced_1.BadRequestError(utils_1.upperFirst(validation_1.validationMessagesFormatters.jsonEmpty())); + } + else if (code === 'MALFORMED_JSON' || error.message === 'Invalid JSON' || error.stack.includes('at JSON.parse')) { + error = new http_errors_enhanced_1.BadRequestError(utils_1.upperFirst(validation_1.validationMessagesFormatters.json())); + } + // Get the status code + let { statusCode, headers } = error; + // Code outside HTTP range + if (statusCode < 100 || statusCode > 599) { + statusCode = http_errors_enhanced_1.INTERNAL_SERVER_ERROR; + } + // Create the body + const body = { + statusCode, + error: http_errors_enhanced_1.messagesByCodes[statusCode], + message: error.message + }; + http_errors_enhanced_1.addAdditionalProperties(body, error); + // Send the error back + // eslint-disable-next-line @typescript-eslint/no-floating-promises + reply + .code(statusCode) + .headers(headers !== null && headers !== void 0 ? headers : {}) + .type('application/json') + .send(body); +} +exports.handleErrors = handleErrors; diff --git a/api/libs/http-error/index.js b/api/libs/http-error/index.js new file mode 100644 index 0000000000..4cffd23625 --- /dev/null +++ b/api/libs/http-error/index.js @@ -0,0 +1,58 @@ +/* eslint-disable */ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.validationMessagesFormatters = exports.niceJoin = exports.convertValidationErrors = void 0; +const fastify_plugin_1 = __importDefault(require("fastify-plugin")); +const handlers_1 = require("./handlers"); +const interfaces_1 = require("./interfaces"); +const validation_1 = require("./validation"); +__exportStar(require("./handlers"), exports); +__exportStar(require("./interfaces"), exports); +var validation_2 = require("./validation"); +Object.defineProperty(exports, "convertValidationErrors", { enumerable: true, get: function () { return validation_2.convertValidationErrors; } }); +Object.defineProperty(exports, "niceJoin", { enumerable: true, get: function () { return validation_2.niceJoin; } }); +Object.defineProperty(exports, "validationMessagesFormatters", { enumerable: true, get: function () { return validation_2.validationMessagesFormatters; } }); +exports.plugin = fastify_plugin_1.default(function (instance, options, done) { + var _a, _b, _c, _d; + const isProduction = process.env.NODE_ENV === 'production'; + const convertResponsesValidationErrors = (_a = options.convertResponsesValidationErrors) !== null && _a !== void 0 ? _a : !isProduction; + const configuration = { + hideUnhandledErrors: (_b = options.hideUnhandledErrors) !== null && _b !== void 0 ? _b : isProduction, + convertValidationErrors: (_c = options.convertValidationErrors) !== null && _c !== void 0 ? _c : true, + responseValidatorCustomizer: options.responseValidatorCustomizer, + allowUndeclaredResponses: (_d = options.allowUndeclaredResponses) !== null && _d !== void 0 ? _d : false + }; + instance.decorate(interfaces_1.kHttpErrorsEnhancedConfiguration, null); + instance.decorateRequest(interfaces_1.kHttpErrorsEnhancedConfiguration, null); + instance.addHook('onRequest', async (request) => { + request[interfaces_1.kHttpErrorsEnhancedConfiguration] = configuration; + }); + instance.setErrorHandler(handlers_1.handleErrors); + // instance.setNotFoundHandler(handlers_1.handleNotFoundError); + if (convertResponsesValidationErrors) { + instance.decorate(interfaces_1.kHttpErrorsEnhancedResponseValidations, []); + instance.addHook('onRoute', validation_1.addResponseValidation); + instance.addHook('onReady', validation_1.compileResponseValidationSchema.bind(instance, configuration)); + } + done(); +}, { name: 'fastify-http-errors-enhanced' }); +exports.default = exports.plugin; +// Fix CommonJS exporting +/* istanbul ignore else */ +if (typeof module !== 'undefined') { + module.exports = exports.plugin; + Object.assign(module.exports, exports); +} diff --git a/api/libs/http-error/interfaces.js b/api/libs/http-error/interfaces.js new file mode 100644 index 0000000000..794d587c46 --- /dev/null +++ b/api/libs/http-error/interfaces.js @@ -0,0 +1,6 @@ +/* eslint-disable */ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.kHttpErrorsEnhancedResponseValidations = exports.kHttpErrorsEnhancedConfiguration = void 0; +exports.kHttpErrorsEnhancedConfiguration = Symbol('fastify-http-errors-enhanced-configuration'); +exports.kHttpErrorsEnhancedResponseValidations = Symbol('fastify-http-errors-enhanced-response-validation'); diff --git a/api/libs/http-error/utils.js b/api/libs/http-error/utils.js new file mode 100644 index 0000000000..caf07313ac --- /dev/null +++ b/api/libs/http-error/utils.js @@ -0,0 +1,31 @@ +/* eslint-disable */ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.get = exports.upperFirst = void 0; +function upperFirst(source) { + if (typeof source !== 'string' || !source.length) { + return source; + } + return source[0].toUpperCase() + source.substring(1); +} +exports.upperFirst = upperFirst; +function get(target, path) { + var _a; + const tokens = path.split('.').map((t) => t.trim()); + for (const token of tokens) { + if (typeof target === 'undefined' || target === null) { + // We're supposed to be still iterating, but the chain is over - Return undefined + target = undefined; + break; + } + const index = token.match(/^(\d+)|(?:\[(\d+)\])$/); + if (index) { + target = target[parseInt((_a = index[1]) !== null && _a !== void 0 ? _a : index[2], 10)]; + } + else { + target = target[token]; + } + } + return target; +} +exports.get = get; diff --git a/api/libs/http-error/validation.js b/api/libs/http-error/validation.js new file mode 100644 index 0000000000..f417be2b00 --- /dev/null +++ b/api/libs/http-error/validation.js @@ -0,0 +1,239 @@ +/* eslint-disable */ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.compileResponseValidationSchema = exports.addResponseValidation = exports.convertValidationErrors = exports.validationMessagesFormatters = exports.niceJoin = void 0; +const ajv_1 = __importDefault(require("ajv")); +const http_errors_enhanced_1 = require("http-errors-enhanced"); +const interfaces_1 = require("./interfaces"); +const utils_1 = require("./utils"); +function niceJoin(array, lastSeparator = ' and ', separator = ', ') { + switch (array.length) { + case 0: + return ''; + case 1: + return array[0]; + case 2: + return array.join(lastSeparator); + default: + return array.slice(0, array.length - 1).join(separator) + lastSeparator + array[array.length - 1]; + } +} +exports.niceJoin = niceJoin; +exports.validationMessagesFormatters = { + contentType: () => 'only JSON payloads are accepted. Please set the "Content-Type" header to start with "application/json"', + json: () => 'the body payload is not a valid JSON', + jsonEmpty: () => 'the JSON body payload cannot be empty if the "Content-Type" header is set', + missing: () => 'must be present', + unknown: () => 'is not a valid property', + uuid: () => 'must be a valid GUID (UUID v4)', + timestamp: () => 'must be a valid ISO 8601 / RFC 3339 timestamp (example: 2018-07-06T12:34:56Z)', + date: () => 'must be a valid ISO 8601 / RFC 3339 date (example: 2018-07-06)', + time: () => 'must be a valid ISO 8601 / RFC 3339 time (example: 12:34:56)', + uri: () => 'must be a valid URI', + hostname: () => 'must be a valid hostname', + ipv4: () => 'must be a valid IPv4', + ipv6: () => 'must be a valid IPv6', + paramType: (type) => { + switch (type) { + case 'integer': + return 'must be a valid integer number'; + case 'number': + return 'must be a valid number'; + case 'boolean': + return 'must be a valid boolean (true or false)'; + case 'object': + return 'must be a object'; + case 'array': + return 'must be an array'; + default: + return 'must be a string'; + } + }, + presentString: () => 'must be a non empty string', + minimum: (min) => `must be a number greater than or equal to ${min}`, + maximum: (max) => `must be a number less than or equal to ${max}`, + minimumProperties(min) { + return min === 1 ? 'cannot be a empty object' : `must be a object with at least ${min} properties`; + }, + maximumProperties(max) { + return max === 0 ? 'must be a empty object' : `must be a object with at most ${max} properties`; + }, + minimumItems(min) { + return min === 1 ? 'cannot be a empty array' : `must be an array with at least ${min} items`; + }, + maximumItems(max) { + return max === 0 ? 'must be a empty array' : `must be an array with at most ${max} items`; + }, + enum: (values) => `must be one of the following values: ${niceJoin(values.map((f) => `"${f}"`), ' or ')}`, + pattern: (pattern) => `must match pattern "${pattern.replace(/\(\?:/g, '(')}"`, + invalidResponseCode: (code) => `This endpoint cannot respond with HTTP status ${code}.`, + invalidResponse: (code) => `The response returned from the endpoint violates its specification for the HTTP status ${code}.`, + invalidFormat: (format) => `must match format "${format}" (format)` +}; +function convertValidationErrors(section, data, validationErrors) { + const errors = {}; + if (section === 'querystring') { + section = 'query'; + } + // For each error + for (const e of validationErrors) { + let message = ''; + let pattern; + let value; + let reason; + // Normalize the key + let key = e.dataPath; + if (key.startsWith('.')) { + key = key.substring(1); + } + // Remove useless quotes + /* istanbul ignore next */ + if (key.startsWith('[') && key.endsWith(']')) { + key = key.substring(1, key.length - 1); + } + // Depending on the type + switch (e.keyword) { + case 'required': + case 'dependencies': + key = e.params.missingProperty; + message = exports.validationMessagesFormatters.missing(); + break; + case 'additionalProperties': + key = e.params.additionalProperty; + message = exports.validationMessagesFormatters.unknown(); + break; + case 'type': + message = exports.validationMessagesFormatters.paramType(e.params.type); + break; + case 'minProperties': + message = exports.validationMessagesFormatters.minimumProperties(e.params.limit); + break; + case 'maxProperties': + message = exports.validationMessagesFormatters.maximumProperties(e.params.limit); + break; + case 'minItems': + message = exports.validationMessagesFormatters.minimumItems(e.params.limit); + break; + case 'maxItems': + message = exports.validationMessagesFormatters.maximumItems(e.params.limit); + break; + case 'minimum': + message = exports.validationMessagesFormatters.minimum(e.params.limit); + break; + case 'maximum': + message = exports.validationMessagesFormatters.maximum(e.params.limit); + break; + case 'enum': + message = exports.validationMessagesFormatters.enum(e.params.allowedValues); + break; + case 'pattern': + pattern = e.params.pattern; + value = utils_1.get(data, key); + if (pattern === '.+' && !value) { + message = exports.validationMessagesFormatters.presentString(); + } + else { + message = exports.validationMessagesFormatters.pattern(e.params.pattern); + } + break; + case 'format': + reason = e.params.format; + // Normalize the key + if (reason === 'date-time') { + reason = 'timestamp'; + } + message = (exports.validationMessagesFormatters[reason] || exports.validationMessagesFormatters.invalidFormat)(reason); + break; + } + // No custom message was found, default to input one replacing the starting verb and adding some path info + if (!message.length) { + message = `${e.message.replace(/^should/, 'must')} (${e.keyword})`; + } + // Remove useless quotes + /* istanbul ignore next */ + if (key.match(/(?:^['"])(?:[^.]+)(?:['"]$)/)) { + key = key.substring(1, key.length - 1); + } + // Fix empty properties + if (!key) { + key = '$root'; + } + key = key.replace(/^\//, ''); + errors[key] = message; + } + return { [section]: errors }; +} +exports.convertValidationErrors = convertValidationErrors; +function addResponseValidation(route) { + var _a; + if (!((_a = route.schema) === null || _a === void 0 ? void 0 : _a.response)) { + return; + } + const validators = {}; + /* + Add these validators to the list of the one to compile once the server is started. + This makes possible to handle shared schemas. + */ + this[interfaces_1.kHttpErrorsEnhancedResponseValidations].push([ + this, + validators, + Object.entries(route.schema.response) + ]); + // Note that this hook is not called for non JSON payloads therefore validation is not possible in such cases + route.preSerialization = async function (request, reply, payload) { + const statusCode = reply.raw.statusCode; + // Never validate error 500 + if (statusCode === http_errors_enhanced_1.INTERNAL_SERVER_ERROR) { + return payload; + } + // No validator, it means the HTTP status is not allowed + const validator = validators[statusCode]; + if (!validator) { + if (request[interfaces_1.kHttpErrorsEnhancedConfiguration].allowUndeclaredResponses) { + return payload; + } + throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponseCode(statusCode)); + } + // Now validate the payload + const valid = validator(payload); + if (!valid) { + throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponse(statusCode), { + failedValidations: convertValidationErrors('response', payload, validator.errors) + }); + } + return payload; + }; +} +exports.addResponseValidation = addResponseValidation; +function compileResponseValidationSchema(configuration) { + // Fix CJS/ESM interoperability + // @ts-expect-error + let AjvConstructor = ajv_1.default; + /* istanbul ignore next */ + if (AjvConstructor.default) { + AjvConstructor = AjvConstructor.default; + } + const hasCustomizer = typeof configuration.responseValidatorCustomizer === 'function'; + for (const [instance, validators, schemas] of this[interfaces_1.kHttpErrorsEnhancedResponseValidations]) { + // @ts-expect-error + const compiler = new AjvConstructor({ + // The fastify defaults, with the exception of removeAdditional and coerceTypes, which have been reversed + removeAdditional: false, + useDefaults: true, + coerceTypes: false, + allErrors: true + }); + compiler.addSchema(Object.values(instance.getSchemas())); + compiler.addKeyword('example'); + if (hasCustomizer) { + configuration.responseValidatorCustomizer(compiler); + } + for (const [code, schema] of schemas) { + validators[code] = compiler.compile(schema); + } + } +} +exports.compileResponseValidationSchema = compileResponseValidationSchema; diff --git a/api/libs/logging.js b/api/libs/logging.js index 9d94780929..f4e08e3954 100644 --- a/api/libs/logging.js +++ b/api/libs/logging.js @@ -1,10 +1,18 @@ +const dayjs = require('dayjs') +const axios = require('axios') + const ApplicationLog = require('../models/Logs/Application') const ServerLog = require('../models/Logs/Server') -const dayjs = require('dayjs') +const Settings = require('../models/Settings') +const { version } = require('../../package.json') function generateTimestamp () { return `${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} ` } +const patterns = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' +].join('|') async function saveAppLog (event, configuration, isError) { try { @@ -12,25 +20,12 @@ async function saveAppLog (event, configuration, isError) { const repoId = configuration.repository.id const branch = configuration.repository.branch if (isError) { - // console.log(event, config, isError) - let clearedEvent = null - - if (event.error) clearedEvent = '[ERROR] ' + generateTimestamp() + event.error.replace(/(\r\n|\n|\r)/gm, '') - else if (event) clearedEvent = '[ERROR] ' + generateTimestamp() + event.replace(/(\r\n|\n|\r)/gm, '') - - try { - await new ApplicationLog({ repoId, branch, deployId, event: clearedEvent }).save() - } catch (error) { - console.log(error) - } + const clearedEvent = '[ERROR 😱] ' + generateTimestamp() + event.replace(new RegExp(patterns, 'g'), '').replace(/(\r\n|\n|\r)/gm, '') + await new ApplicationLog({ repoId, branch, deployId, event: clearedEvent }).save() } else { if (event && event !== '\n') { - const clearedEvent = '[INFO] ' + generateTimestamp() + event.replace(/(\r\n|\n|\r)/gm, '') - try { - await new ApplicationLog({ repoId, branch, deployId, event: clearedEvent }).save() - } catch (error) { - console.log(error) - } + const clearedEvent = '[INFO] ' + generateTimestamp() + event.replace(new RegExp(patterns, 'g'), '').replace(/(\r\n|\n|\r)/gm, '') + await new ApplicationLog({ repoId, branch, deployId, event: clearedEvent }).save() } } } catch (error) { @@ -39,20 +34,14 @@ async function saveAppLog (event, configuration, isError) { } } -async function saveServerLog ({ event, configuration, type }) { - try { - if (configuration) { - const deployId = configuration.general.deployId - const repoId = configuration.repository.id - const branch = configuration.repository.branch - await new ApplicationLog({ repoId, branch, deployId, event: `[SERVER ERROR 😖]: ${event}` }).save() - } - await new ServerLog({ event, type }).save() - } catch (error) { - // Hmm. - } -} +async function saveServerLog (error) { + const settings = await Settings.findOne({ applicationName: 'coolify' }) + const payload = { message: error.message, stack: error.stack, type: error.type || 'spaghetticode', version } + const found = await ServerLog.find(payload) + if (found.length === 0 && error.message) await new ServerLog(payload).save() + if (settings && settings.sendErrors && process.env.NODE_ENV === 'production') await axios.post('https://errors.coollabs.io/api/error', payload) +} module.exports = { saveAppLog, saveServerLog diff --git a/api/models/Logs/Server.js b/api/models/Logs/Server.js index 3a1b5607e4..a5abc460e3 100644 --- a/api/models/Logs/Server.js +++ b/api/models/Logs/Server.js @@ -2,10 +2,11 @@ const mongoose = require('mongoose') const { version } = require('../../../package.json') const logSchema = mongoose.Schema( { - version: { type: String, required: true, default: version }, - type: { type: String, required: true, enum: ['API', 'UPGRADE-P-1', 'UPGRADE-P-2'], default: 'API' }, - event: { type: String, required: true }, - seen: { type: Boolean, required: true, default: false } + version: { type: String, default: version }, + type: { type: String, required: true }, + message: { type: String, required: true }, + stack: { type: String }, + seen: { type: Boolean, default: false } }, { timestamps: { createdAt: 'createdAt', updatedAt: false } } ) diff --git a/api/models/Settings.js b/api/models/Settings.js index 14042b9129..c6d424364e 100644 --- a/api/models/Settings.js +++ b/api/models/Settings.js @@ -3,7 +3,8 @@ const mongoose = require('mongoose') const settingsSchema = mongoose.Schema( { applicationName: { type: String, required: true, default: 'coolify' }, - allowRegistration: { type: Boolean, required: true, default: false } + allowRegistration: { type: Boolean, required: true, default: false }, + sendErrors: { type: Boolean, required: true, default: true } }, { timestamps: true } ) diff --git a/api/routes/v1/application/check.js b/api/routes/v1/application/check.js index f53a91df9d..0ea8eb853c 100644 --- a/api/routes/v1/application/check.js +++ b/api/routes/v1/application/check.js @@ -1,15 +1,11 @@ -const { verifyUserId } = require('../../../libs/common') const { setDefaultConfiguration } = require('../../../libs/applications/configuration') const { docker } = require('../../../libs/docker') +const { saveServerLog } = require('../../../libs/logging') module.exports = async function (fastify) { fastify.post('/', async (request, reply) => { try { - if (!await verifyUserId(request.headers.authorization)) { - reply.code(500).send({ error: 'Invalid request' }) - return - } const configuration = setDefaultConfiguration(request.body) const services = (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application') @@ -34,7 +30,8 @@ module.exports = async function (fastify) { } return { message: 'OK' } } catch (error) { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } }) } diff --git a/api/routes/v1/application/deploy/index.js b/api/routes/v1/application/deploy/index.js index 2204aeb670..8bcc84dbdf 100644 --- a/api/routes/v1/application/deploy/index.js +++ b/api/routes/v1/application/deploy/index.js @@ -1,37 +1,16 @@ -const { verifyUserId, cleanupTmp } = require('../../../../libs/common') const Deployment = require('../../../../models/Deployment') +const ApplicationLog = require('../../../../models/Logs/Application') +const { verifyUserId, cleanupTmp } = require('../../../../libs/common') const { queueAndBuild } = require('../../../../libs/applications') const { setDefaultConfiguration, precheckDeployment } = require('../../../../libs/applications/configuration') const { docker } = require('../../../../libs/docker') +const { saveServerLog } = require('../../../../libs/logging') const cloneRepository = require('../../../../libs/applications/github/cloneRepository') module.exports = async function (fastify) { - // const postSchema = { - // body: { - // type: "object", - // properties: { - // ref: { type: "string" }, - // repository: { - // type: "object", - // properties: { - // id: { type: "number" }, - // full_name: { type: "string" }, - // }, - // required: ["id", "full_name"], - // }, - // installation: { - // type: "object", - // properties: { - // id: { type: "number" }, - // }, - // required: ["id"], - // }, - // }, - // required: ["ref", "repository", "installation"], - // }, - // }; fastify.post('/', async (request, reply) => { + let configuration try { await verifyUserId(request.headers.authorization) } catch (error) { @@ -40,7 +19,10 @@ module.exports = async function (fastify) { } try { const services = (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application') - const configuration = setDefaultConfiguration(request.body) + configuration = setDefaultConfiguration(request.body) + if (!configuration) { + throw new Error('Whaat?') + } await cloneRepository(configuration) const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment({ services, configuration }) @@ -64,11 +46,21 @@ module.exports = async function (fastify) { return } - queueAndBuild(configuration, imageChanged) - - reply.code(201).send({ message: 'Deployment queued.', nickname: configuration.general.nickname, name: configuration.build.container.name }) + reply.code(201).send({ message: 'Deployment queued.', nickname: configuration.general.nickname, name: configuration.build.container.name, deployId: configuration.general.deployId }) + await queueAndBuild(configuration, imageChanged) } catch (error) { - throw { error, type: 'server' } + const { id, organization, name, branch } = configuration.repository + const { domain } = configuration.publish + const { deployId } = configuration.general + await Deployment.findOneAndUpdate( + { repoId: id, branch, deployId, organization, name, domain }, + { repoId: id, branch, deployId, organization, name, domain, progress: 'failed' }) + cleanupTmp(configuration.general.workdir) + if (error.name) { + if (error.message && error.stack) await saveServerLog(error) + if (reply.sent) await new ApplicationLog({ repoId: id, branch, deployId, event: `[ERROR 😖]: ${error.stack}` }).save() + } + throw new Error(error) } }) } diff --git a/api/routes/v1/application/deploy/logs.js b/api/routes/v1/application/deploy/logs.js index 234015f6d5..da4e4b38b3 100644 --- a/api/routes/v1/application/deploy/logs.js +++ b/api/routes/v1/application/deploy/logs.js @@ -39,7 +39,7 @@ module.exports = async function (fastify) { }) return finalLogs } catch (error) { - throw { error, type: 'server' } + throw new Error(error) } }) diff --git a/api/routes/v1/application/logs.js b/api/routes/v1/application/logs.js index 0396a22b32..155f888414 100644 --- a/api/routes/v1/application/logs.js +++ b/api/routes/v1/application/logs.js @@ -1,4 +1,5 @@ const { docker } = require('../../../libs/docker') +const { saveServerLog } = require('../../../libs/logging') module.exports = async function (fastify) { fastify.get('/', async (request, reply) => { @@ -8,7 +9,8 @@ module.exports = async function (fastify) { const logs = (await service.logs({ stdout: true, stderr: true, timestamps: true })).toString().split('\n').map(l => l.slice(8)).filter((a) => a) return { logs } } catch (error) { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } }) } diff --git a/api/routes/v1/dashboard/index.js b/api/routes/v1/dashboard/index.js index f5e7c6c5ae..4205a66d81 100644 --- a/api/routes/v1/dashboard/index.js +++ b/api/routes/v1/dashboard/index.js @@ -1,6 +1,7 @@ const { docker } = require('../../../libs/docker') const Deployment = require('../../../models/Deployment') const ServerLog = require('../../../models/Logs/Server') +const { saveServerLog } = require('../../../libs/logging') module.exports = async function (fastify) { fastify.get('/', async (request, reply) => { @@ -21,10 +22,8 @@ module.exports = async function (fastify) { } } ]) - const serverLogs = await ServerLog.find() const services = await docker.engine.listServices() - let applications = services.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && r.Spec.Labels.configuration) let databases = services.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'database' && r.Spec.Labels.configuration) applications = applications.map(r => { @@ -56,7 +55,8 @@ module.exports = async function (fastify) { if (error.code === 'ENOENT' && error.errno === -2) { throw new Error(`Docker service unavailable at ${error.address}.`) } else { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } } }) diff --git a/api/routes/v1/databases/index.js b/api/routes/v1/databases/index.js index 7ca554c13a..9cdac684fd 100644 --- a/api/routes/v1/databases/index.js +++ b/api/routes/v1/databases/index.js @@ -3,6 +3,7 @@ const fs = require('fs').promises const cuid = require('cuid') const { docker } = require('../../../libs/docker') const { execShellAsync } = require('../../../libs/common') +const { saveServerLog } = require('../../../libs/logging') const { uniqueNamesGenerator, adjectives, colors, animals } = require('unique-names-generator') const generator = require('generate-password') @@ -165,7 +166,8 @@ module.exports = async function (fastify) { `cat ${configuration.general.workdir}/stack.yml | docker stack deploy -c - ${configuration.general.deployId}` ) } catch (error) { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } }) diff --git a/api/routes/v1/login/github.js b/api/routes/v1/login/github.js index 0d395a1960..bd621faad7 100644 --- a/api/routes/v1/login/github.js +++ b/api/routes/v1/login/github.js @@ -4,6 +4,8 @@ const Settings = require('../../../models/Settings') const cuid = require('cuid') const mongoose = require('mongoose') const jwt = require('jsonwebtoken') +const { saveServerLog } = require('../../../libs/logging') + module.exports = async function (fastify) { const githubCodeSchema = { schema: { @@ -59,8 +61,12 @@ module.exports = async function (fastify) { avatar: avatar_url, uid }) + const defaultSettings = new Settings({ + _id: new mongoose.Types.ObjectId() + }) try { await newUser.save() + await defaultSettings.save() } catch (e) { console.log(e) reply.code(500).send({ success: false, error: e }) @@ -111,8 +117,8 @@ module.exports = async function (fastify) { return } } catch (error) { - console.log(error) - reply.code(500).send({ success: false, error: error.message }) + await saveServerLog(error) + throw new Error(error) } }) fastify.get('/success', async (request, reply) => { diff --git a/api/routes/v1/server/index.js b/api/routes/v1/server/index.js index a43a6b9d49..278b9a3bf1 100644 --- a/api/routes/v1/server/index.js +++ b/api/routes/v1/server/index.js @@ -8,7 +8,7 @@ module.exports = async function (fastify) { serverLogs } } catch (error) { - throw { error, type: 'server' } + throw new Error(error) } }) } diff --git a/api/routes/v1/settings/index.js b/api/routes/v1/settings/index.js index ac502e9d78..7a5dbd59d5 100644 --- a/api/routes/v1/settings/index.js +++ b/api/routes/v1/settings/index.js @@ -1,13 +1,16 @@ const Settings = require('../../../models/Settings') +const { saveServerLog } = require('../../../libs/logging') + module.exports = async function (fastify) { const applicationName = 'coolify' const postSchema = { body: { type: 'object', properties: { - allowRegistration: { type: 'boolean' } + allowRegistration: { type: 'boolean' }, + sendErrors: { type: 'boolean' } }, - required: ['allowRegistration'] + required: [] } } @@ -25,7 +28,8 @@ module.exports = async function (fastify) { settings } } catch (error) { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } }) @@ -38,7 +42,8 @@ module.exports = async function (fastify) { ).select('-_id -__v') reply.code(201).send({ settings }) } catch (error) { - throw { error, type: 'server' } + await saveServerLog(error) + throw new Error(error) } }) } diff --git a/api/routes/v1/upgrade/index.js b/api/routes/v1/upgrade/index.js index 6ed1566804..49c3c016ee 100644 --- a/api/routes/v1/upgrade/index.js +++ b/api/routes/v1/upgrade/index.js @@ -4,9 +4,9 @@ const { saveServerLog } = require('../../../libs/logging') module.exports = async function (fastify) { fastify.get('/', async (request, reply) => { const upgradeP1 = await execShellAsync('bash -c "$(curl -fsSL https://get.coollabs.io/coolify/upgrade-p1.sh)"') - await saveServerLog({ event: upgradeP1, type: 'UPGRADE-P-1' }) + await saveServerLog({ message: upgradeP1, type: 'UPGRADE-P-1' }) reply.code(200).send('I\'m trying, okay?') const upgradeP2 = await execShellAsync('docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -u root coolify bash -c "$(curl -fsSL https://get.coollabs.io/coolify/upgrade-p2.sh)"') - await saveServerLog({ event: upgradeP2, type: 'UPGRADE-P-2' }) + await saveServerLog({ message: upgradeP2, type: 'UPGRADE-P-2' }) }) } diff --git a/api/routes/v1/webhooks/deploy.js b/api/routes/v1/webhooks/deploy.js index 540bf17099..99b758ddef 100644 --- a/api/routes/v1/webhooks/deploy.js +++ b/api/routes/v1/webhooks/deploy.js @@ -1,6 +1,10 @@ const crypto = require('crypto') const { cleanupTmp } = require('../../../libs/common') + const Deployment = require('../../../models/Deployment') +const ApplicationLog = require('../../../models/Logs/Application') +const ServerLog = require('../../../models/Logs/Server') + const { queueAndBuild } = require('../../../libs/applications') const { setDefaultConfiguration, precheckDeployment } = require('../../../libs/applications/configuration') const { docker } = require('../../../libs/docker') @@ -33,6 +37,7 @@ module.exports = async function (fastify) { } } fastify.post('/', { schema: postSchema }, async (request, reply) => { + let configuration const hmac = crypto.createHmac('sha256', fastify.config.GITHUP_APP_WEBHOOK_SECRET) const digest = Buffer.from('sha256=' + hmac.update(JSON.stringify(request.body)).digest('hex'), 'utf8') const checksum = Buffer.from(request.headers['x-hub-signature-256'], 'utf8') @@ -48,7 +53,7 @@ module.exports = async function (fastify) { try { const services = (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application') - let configuration = services.find(r => { + configuration = services.find(r => { if (request.body.ref.startsWith('refs')) { const branch = request.body.ref.split('/')[2] if ( @@ -88,12 +93,27 @@ module.exports = async function (fastify) { reply.code(200).send({ message: 'Already in the queue.' }) return } - queueAndBuild(configuration, imageChanged) reply.code(201).send({ message: 'Deployment queued.', nickname: configuration.general.nickname, name: configuration.build.container.name }) } catch (error) { - throw { error, type: 'server' } + const { id, organization, name, branch } = configuration.repository + const { domain } = configuration.publish + const { deployId } = configuration.general + await Deployment.findOneAndUpdate( + { repoId: id, branch, deployId, organization, name, domain }, + { repoId: id, branch, deployId, organization, name, domain, progress: 'failed' }) + cleanupTmp(configuration.general.workdir) + if (error.name === 'Error') { + // Error during runtime + await new ApplicationLog({ repoId: id, branch, deployId, event: `[ERROR 😖]: ${error.stack}` }).save() + } else { + // Error in my code + const payload = { message: error.message, stack: error.stack, type: 'spaghetticode' } + if (error.message && error.stack) await new ServerLog(payload).save() + if (reply.sent) await new ApplicationLog({ repoId: id, branch, deployId, event: `[ERROR 😖]: ${error.stack}` }).save() + } + throw new Error(error) } }) } diff --git a/api/server.js b/api/server.js index 628cceb8c1..4f25e93ab8 100644 --- a/api/server.js +++ b/api/server.js @@ -1,21 +1,26 @@ require('dotenv').config() const fs = require('fs') const util = require('util') +const axios = require('axios') +const mongoose = require('mongoose') +const path = require('path') const { saveServerLog } = require('./libs/logging') const { execShellAsync } = require('./libs/common') const { purgeImagesContainers, cleanupStuckedDeploymentsInDB } = require('./libs/applications/cleanup') -const Deployment = require('./models/Deployment') const fastify = require('fastify')({ - logger: { level: 'error' } + trustProxy: true, + logger: { + level: 'error' + } }) -const mongoose = require('mongoose') -const path = require('path') +fastify.register(require('../api/libs/http-error')) + const { schema } = require('./schema') -process.on('unhandledRejection', (reason, p) => { - console.log(reason) - console.log(p) +process.on('unhandledRejection', async (reason, p) => { + await saveServerLog({ message: reason.message, type: 'unhandledRejection' }) }) + fastify.register(require('fastify-env'), { schema, dotenv: true @@ -36,18 +41,6 @@ if (process.env.NODE_ENV === 'production') { } fastify.register(require('./app'), { prefix: '/api/v1' }) -fastify.setErrorHandler(async (error, request, reply) => { - if (error.statusCode) { - reply.status(error.statusCode).send({ message: error.message } || { message: 'Something is NOT okay. Are you okay?' }) - } else { - reply.status(500).send({ message: error.message } || { message: 'Something is NOT okay. Are you okay?' }) - } - try { - await saveServerLog({ event: error }) - } catch (error) { - // - } -}) if (process.env.NODE_ENV === 'production') { mongoose.connect( @@ -91,6 +84,14 @@ mongoose.connection.once('open', async function () { fastify.listen(3001) console.log('Coolify API is up and running in development.') } + try { + const { main } = (await axios.get('https://get.coollabs.io/version.json')).data.coolify + if (main.clearServerLogs) { + await mongoose.connection.db.dropCollection('logs-servers') + } + } catch (error) { + // Could not cleanup logs-servers collection + } // On start cleanup inprogress/queued deployments. try { await cleanupStuckedDeploymentsInDB() @@ -98,8 +99,8 @@ mongoose.connection.once('open', async function () { // Could not cleanup DB 🤔 } try { - // Doing because I do not want to prune these images. Prune skip coolify-reserve labeled images. - const basicImages = ['nginx:stable-alpine', 'node:lts', 'ubuntu:20.04'] + // Doing because I do not want to prune these images. Prune skips coolify-reserve labeled images. + const basicImages = ['nginx:stable-alpine', 'node:lts', 'ubuntu:20.04', 'php:apache', 'rust:latest'] for (const image of basicImages) { await execShellAsync(`echo "FROM ${image}" | docker build --label coolify-reserve=true -t ${image} -`) } diff --git a/install/Dockerfile-new b/install/Dockerfile-new index 947123030f..c0c46b08e4 100644 --- a/install/Dockerfile-new +++ b/install/Dockerfile-new @@ -1,24 +1,15 @@ -FROM ubuntu:20.04 as binaries +FROM node:lts LABEL coolify-preserve=true -RUN apt update && apt install -y curl gnupg2 ca-certificates -RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - -RUN echo 'deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable' >> /etc/apt/sources.list +WORKDIR /usr/src/app +RUN curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-20.10.6.tgz | tar -xzvf - docker/docker -C . --strip-components 1 +RUN mv /usr/src/app/docker /usr/bin/docker RUN curl -L https://github.com/a8m/envsubst/releases/download/v1.2.0/envsubst-`uname -s`-`uname -m` -o /usr/bin/envsubst RUN curl -L https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -o /usr/bin/jq -RUN chmod +x /usr/bin/envsubst /usr/bin/jq -RUN apt update && apt install -y docker-ce-cli && apt clean all - -FROM node:lts -WORKDIR /usr/src/app -LABEL coolify-preserve=true -COPY --from=binaries /usr/bin/docker /usr/bin/docker -COPY --from=binaries /usr/bin/envsubst /usr/bin/envsubst -COPY --from=binaries /usr/bin/jq /usr/bin/jq -COPY . . -RUN curl -f https://get.pnpm.io/v6.js | node - add --global pnpm@6 +RUN chmod +x /usr/bin/envsubst /usr/bin/jq /usr/bin/docker +RUN curl -f https://get.pnpm.io/v6.js | node - add --global pnpm +COPY ./*package.json . RUN pnpm install +COPY . . RUN pnpm build -RUN rm -fr node_modules .pnpm-store -RUN pnpm install -P CMD ["pnpm", "start"] EXPOSE 3000 \ No newline at end of file diff --git a/install/coolify-template-dev.yml b/install/coolify-template-dev.yml new file mode 100644 index 0000000000..8fa26c0660 --- /dev/null +++ b/install/coolify-template-dev.yml @@ -0,0 +1,73 @@ +version: '3.8' + +services: + proxy: + image: traefik:v2.4 + hostname: coollabs-proxy + ports: + - target: 80 + published: 80 + protocol: tcp + mode: host + - target: 443 + published: 443 + protocol: tcp + mode: host + - target: 8080 + published: 8080 + protocol: tcp + mode: host + command: + - --api.insecure=true + - --api.dashboard=true + - --api.debug=true + - --log.level=ERROR + - --providers.docker=true + - --providers.docker.swarmMode=true + - --providers.docker.exposedbydefault=false + - --providers.docker.network=${DOCKER_NETWORK} + - --providers.docker.swarmModeRefreshSeconds=1s + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + networks: + - ${DOCKER_NETWORK} + deploy: + update_config: + parallelism: 1 + delay: 10s + order: start-first + replicas: 1 + placement: + constraints: + - node.role == manager + labels: + - "traefik.enable=true" + - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.services.traefik.loadbalancer.server.port=80" + - "traefik.http.services.traefik.loadbalancer.server.port=443" + + # Global redirect www to non-www + - "traefik.http.routers.www-catchall.rule=hostregexp(`{host:www.(.+)}`)" + - "traefik.http.routers.www-catchall.entrypoints=web" + - "traefik.http.routers.www-catchall.middlewares=redirect-www-to-nonwww" + - "traefik.http.middlewares.redirect-www-to-nonwww.redirectregex.regex=^http://(?:www\\.)?(.+)" + - "traefik.http.middlewares.redirect-www-to-nonwww.redirectregex.replacement=http://$$$${1}" + + # Global redirect http to https + - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)" + - "traefik.http.routers.http-catchall.entrypoints=web" + - "traefik.http.routers.http-catchall.middlewares=redirect-to-https" + + - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + - "traefik.http.middlewares.global-compress.compress=true" + +networks: + ${DOCKER_NETWORK}: + driver: overlay + name: ${DOCKER_NETWORK} + external: true + diff --git a/install/coolify-template.yml b/install/coolify-template.yml index 574ade0e02..92f4213706 100644 --- a/install/coolify-template.yml +++ b/install/coolify-template.yml @@ -2,7 +2,7 @@ version: '3.8' services: proxy: - image: traefik:v2.3 + image: traefik:v2.4 hostname: coollabs-proxy ports: - target: 80 diff --git a/package.json b/package.json index 7c7a802ee2..b0de167297 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.", - "version": "1.0.6", + "version": "1.0.7", "license": "AGPL-3.0", "scripts": { "lint": "standard", @@ -19,6 +19,7 @@ "@iarna/toml": "^2.2.5", "@roxi/routify": "^2.15.1", "@zerodevx/svelte-toast": "^0.2.1", + "ajv": "^8.1.0", "axios": "^0.21.1", "commander": "^7.2.0", "compare-versions": "^3.6.0", @@ -33,6 +34,7 @@ "fastify-plugin": "^3.0.0", "fastify-static": "^4.0.1", "generate-password": "^1.6.0", + "http-errors-enhanced": "^0.7.0", "js-yaml": "^4.0.0", "jsonwebtoken": "^8.5.1", "mongoose": "^5.12.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f09c6c74f6..a1706c14f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,7 @@ specifiers: '@iarna/toml': ^2.2.5 '@roxi/routify': ^2.15.1 '@zerodevx/svelte-toast': ^0.2.1 + ajv: ^8.1.0 axios: ^0.21.1 commander: ^7.2.0 compare-versions: ^3.6.0 @@ -18,6 +19,7 @@ specifiers: fastify-plugin: ^3.0.0 fastify-static: ^4.0.1 generate-password: ^1.6.0 + http-errors-enhanced: ^0.7.0 js-yaml: ^4.0.0 jsonwebtoken: ^8.5.1 mongodb-memory-server-core: ^6.9.6 @@ -44,6 +46,7 @@ dependencies: '@iarna/toml': 2.2.5 '@roxi/routify': 2.15.1 '@zerodevx/svelte-toast': 0.2.1 + ajv: 8.1.0 axios: 0.21.1 commander: 7.2.0 compare-versions: 3.6.0 @@ -58,9 +61,10 @@ dependencies: fastify-plugin: 3.0.0 fastify-static: 4.0.1 generate-password: 1.6.0 - js-yaml: 4.0.0 + http-errors-enhanced: 0.7.0 + js-yaml: 4.1.0 jsonwebtoken: 8.5.1 - mongoose: 5.12.3 + mongoose: 5.12.4 shelljs: 0.8.4 svelte-select: 3.17.0 unique-names-generator: 4.4.0 @@ -69,8 +73,8 @@ devDependencies: mongodb-memory-server-core: 6.9.6 nodemon: 2.0.7 npm-run-all: 4.1.5 - postcss: 8.2.9 - postcss-import: 14.0.1_postcss@8.2.9 + postcss: 8.2.10 + postcss-import: 14.0.1_postcss@8.2.10 postcss-load-config: 3.0.1 postcss-preset-env: 6.7.0 prettier: 2.2.1 @@ -78,9 +82,9 @@ devDependencies: standard: 16.0.3 svelte: 3.37.0 svelte-hmr: 0.14.0_svelte@3.37.0 - svelte-preprocess: 4.7.0_fa8d64e4f515eee295d8f0f45fceadd2 - svite: 0.8.1_d334b093211aa94b4e678204453b11ae - tailwindcss: 2.1.1_postcss@8.2.9 + svelte-preprocess: 4.7.0_4a3768216fad352756a1a13607f73a31 + svite: 0.8.1_79b75b88a1dd913f13bddff6afa5f1de + tailwindcss: 2.1.1_postcss@8.2.10 packages: @@ -102,14 +106,14 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.13.12: - resolution: {integrity: sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==} + /@babel/parser/7.13.15: + resolution: {integrity: sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==} engines: {node: '>=6.0.0'} hasBin: true dev: true - /@babel/types/7.13.12: - resolution: {integrity: sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==} + /@babel/types/7.13.14: + resolution: {integrity: sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==} dependencies: '@babel/helper-validator-identifier': 7.12.11 lodash: 4.17.21 @@ -189,44 +193,44 @@ packages: fastq: 1.11.0 dev: true - /@rollup/plugin-commonjs/16.0.0_rollup@2.42.3: + /@rollup/plugin-commonjs/16.0.0_rollup@2.45.2: resolution: {integrity: sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==} engines: {node: '>= 8.0.0'} peerDependencies: rollup: ^2.30.0 dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.42.3 + '@rollup/pluginutils': 3.1.0_rollup@2.45.2 commondir: 1.0.1 estree-walker: 2.0.2 glob: 7.1.6 is-reference: 1.2.1 magic-string: 0.25.7 resolve: 1.20.0 - rollup: 2.42.3 + rollup: 2.45.2 dev: true - /@rollup/plugin-json/4.1.0_rollup@2.42.3: + /@rollup/plugin-json/4.1.0_rollup@2.45.2: resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==} peerDependencies: rollup: ^1.20.0 || ^2.0.0 dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.42.3 - rollup: 2.42.3 + '@rollup/pluginutils': 3.1.0_rollup@2.45.2 + rollup: 2.45.2 dev: true - /@rollup/plugin-node-resolve/10.0.0_rollup@2.42.3: + /@rollup/plugin-node-resolve/10.0.0_rollup@2.45.2: resolution: {integrity: sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A==} engines: {node: '>= 10.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0 dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.42.3 + '@rollup/pluginutils': 3.1.0_rollup@2.45.2 '@types/resolve': 1.17.1 builtin-modules: 3.2.0 deepmerge: 4.2.2 is-module: 1.0.0 resolve: 1.20.0 - rollup: 2.42.3 + rollup: 2.45.2 dev: true /@rollup/pluginutils/3.1.0: @@ -237,10 +241,10 @@ packages: dependencies: '@types/estree': 0.0.39 estree-walker: 1.0.1 - picomatch: 2.2.2 + picomatch: 2.2.3 dev: true - /@rollup/pluginutils/3.1.0_rollup@2.42.3: + /@rollup/pluginutils/3.1.0_rollup@2.45.2: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} peerDependencies: @@ -248,8 +252,8 @@ packages: dependencies: '@types/estree': 0.0.39 estree-walker: 1.0.1 - picomatch: 2.2.2 - rollup: 2.42.3 + picomatch: 2.2.3 + rollup: 2.45.2 dev: true /@rollup/pluginutils/4.1.0: @@ -259,18 +263,18 @@ packages: rollup: ^1.20.0||^2.0.0 dependencies: estree-walker: 2.0.2 - picomatch: 2.2.2 + picomatch: 2.2.3 dev: true - /@rollup/pluginutils/4.1.0_rollup@2.42.3: + /@rollup/pluginutils/4.1.0_rollup@2.45.2: resolution: {integrity: sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==} engines: {node: '>= 8.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0 dependencies: estree-walker: 2.0.2 - picomatch: 2.2.2 - rollup: 2.42.3 + picomatch: 2.2.3 + rollup: 2.45.2 dev: true /@roxi/routify/2.15.1: @@ -278,7 +282,7 @@ packages: hasBin: true dependencies: '@roxi/ssr': 0.2.1 - '@types/node': 12.20.6 + '@types/node': 12.20.10 chalk: 4.1.0 cheap-watch: 1.0.3 commander: 7.2.0 @@ -286,7 +290,7 @@ packages: esm: 3.2.25 fs-extra: 9.1.0 log-symbols: 3.0.0 - picomatch: 2.2.2 + picomatch: 2.2.3 rollup-pluginutils: 2.8.2 transitivePeerDependencies: - canvas @@ -296,7 +300,7 @@ packages: resolution: {integrity: sha512-C86xWJOmtCGZr/U4MURqePM0oDKFkTlLeEyT07R+7jSKvREKZ2manJAeAebYudJLYEGITEPGqObhRan32bUUbg==} dependencies: bufferutil: 4.0.3 - jsdom: 16.5.1_c70f8fc5586dd378b8c866035dbe710b + jsdom: 16.5.3_c70f8fc5586dd378b8c866035dbe710b node-fetch: 2.6.1 utf-8-validate: 5.0.4 transitivePeerDependencies: @@ -318,26 +322,26 @@ packages: /@types/accepts/1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/body-parser/1.19.0: resolution: {integrity: sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==} dependencies: '@types/connect': 3.4.34 - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/bson/4.0.3: resolution: {integrity: sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: false /@types/connect/3.4.34: resolution: {integrity: sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/content-disposition/0.5.3: @@ -350,21 +354,21 @@ packages: '@types/connect': 3.4.34 '@types/express': 4.17.11 '@types/keygrip': 1.0.2 - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/estree/0.0.39: resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} dev: true - /@types/estree/0.0.46: - resolution: {integrity: sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==} + /@types/estree/0.0.47: + resolution: {integrity: sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==} dev: true /@types/express-serve-static-core/4.17.19: resolution: {integrity: sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 '@types/qs': 6.9.6 '@types/range-parser': 1.2.3 dev: true @@ -389,7 +393,7 @@ packages: /@types/http-proxy/1.17.5: resolution: {integrity: sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/json5/0.0.29: @@ -399,7 +403,7 @@ packages: /@types/jsonwebtoken/8.5.1: resolution: {integrity: sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: false /@types/keygrip/1.0.2: @@ -422,7 +426,7 @@ packages: '@types/http-errors': 1.8.0 '@types/keygrip': 1.0.2 '@types/koa-compose': 3.2.5 - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/lru-cache/5.1.0: @@ -433,19 +437,19 @@ packages: resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} dev: true - /@types/mongodb/3.6.10: - resolution: {integrity: sha512-BkwAHFiZSSWdTIqbUVGmgvIsiXXjqAketeK7Izy7oSs6G3N8Bn993tK9eq6QEovQDx6OQ2FGP2KWDDxBzdlJ6Q==} + /@types/mongodb/3.6.12: + resolution: {integrity: sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==} dependencies: '@types/bson': 4.0.3 - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: false - /@types/node/12.20.6: - resolution: {integrity: sha512-sRVq8d+ApGslmkE9e3i+D3gFGk7aZHAT+G4cIpIEdLJYPsWiSPwcAnJEjddLQQDqV3Ra2jOclX/Sv6YrvGYiWA==} + /@types/node/12.20.10: + resolution: {integrity: sha512-TxCmnSSppKBBOzYzPR2BR25YlX5Oay8z2XGwFBInuA/Co0V9xJhLlW4kjbxKtgeNo3NOMbQP1A5Rc03y+XecPw==} dev: false - /@types/node/14.14.35: - resolution: {integrity: sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==} + /@types/node/14.14.41: + resolution: {integrity: sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==} /@types/parse-json/4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} @@ -466,97 +470,97 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/sass/1.16.0: resolution: {integrity: sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/serve-static/1.13.9: resolution: {integrity: sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==} dependencies: '@types/mime': 1.3.2 - '@types/node': 14.14.35 + '@types/node': 14.14.41 dev: true /@types/tmp/0.2.0: resolution: {integrity: sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==} dev: true - /@vue/compiler-core/3.0.7: - resolution: {integrity: sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ==} + /@vue/compiler-core/3.0.11: + resolution: {integrity: sha512-6sFj6TBac1y2cWCvYCA8YzHJEbsVkX7zdRs/3yK/n1ilvRqcn983XvpBbnN3v4mZ1UiQycTvOiajJmOgN9EVgw==} dependencies: - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 - '@vue/shared': 3.0.7 + '@babel/parser': 7.13.15 + '@babel/types': 7.13.14 + '@vue/shared': 3.0.11 estree-walker: 2.0.2 source-map: 0.6.1 dev: true - /@vue/compiler-dom/3.0.7: - resolution: {integrity: sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw==} + /@vue/compiler-dom/3.0.11: + resolution: {integrity: sha512-+3xB50uGeY5Fv9eMKVJs2WSRULfgwaTJsy23OIltKgMrynnIj8hTYY2UL97HCoz78aDw1VDXdrBQ4qepWjnQcw==} dependencies: - '@vue/compiler-core': 3.0.7 - '@vue/shared': 3.0.7 + '@vue/compiler-core': 3.0.11 + '@vue/shared': 3.0.11 dev: true - /@vue/compiler-sfc/3.0.7_vue@3.0.7: - resolution: {integrity: sha512-37/QILpGE+J3V+bP9Slg9e6xGqfk+MmS2Yj8ChR4fS0/qWUU/YoYHE0GPIzjmBdH0JVOOmJqunxowIXmqNiHng==} + /@vue/compiler-sfc/3.0.11_vue@3.0.11: + resolution: {integrity: sha512-7fNiZuCecRleiyVGUWNa6pn8fB2fnuJU+3AGjbjl7r1P5wBivfl02H4pG+2aJP5gh2u+0wXov1W38tfWOphsXw==} peerDependencies: - vue: 3.0.7 - dependencies: - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 - '@vue/compiler-core': 3.0.7 - '@vue/compiler-dom': 3.0.7 - '@vue/compiler-ssr': 3.0.7 - '@vue/shared': 3.0.7 + vue: 3.0.11 + dependencies: + '@babel/parser': 7.13.15 + '@babel/types': 7.13.14 + '@vue/compiler-core': 3.0.11 + '@vue/compiler-dom': 3.0.11 + '@vue/compiler-ssr': 3.0.11 + '@vue/shared': 3.0.11 consolidate: 0.16.0 estree-walker: 2.0.2 hash-sum: 2.0.0 lru-cache: 5.1.1 magic-string: 0.25.7 merge-source-map: 1.1.0 - postcss: 8.2.8 - postcss-modules: 4.0.0_postcss@8.2.8 + postcss: 8.2.10 + postcss-modules: 4.0.0_postcss@8.2.10 postcss-selector-parser: 6.0.4 source-map: 0.6.1 - vue: 3.0.7 + vue: 3.0.11 dev: true - /@vue/compiler-ssr/3.0.7: - resolution: {integrity: sha512-nHRbHeSpfXwjypettjrA16TjgfDcPEwq3m/zHnGyLC1QqdLtklXmpSM43/CPwwTCRa/qdt0pldJf22MiCEuTSQ==} + /@vue/compiler-ssr/3.0.11: + resolution: {integrity: sha512-66yUGI8SGOpNvOcrQybRIhl2M03PJ+OrDPm78i7tvVln86MHTKhM3ERbALK26F7tXl0RkjX4sZpucCpiKs3MnA==} dependencies: - '@vue/compiler-dom': 3.0.7 - '@vue/shared': 3.0.7 + '@vue/compiler-dom': 3.0.11 + '@vue/shared': 3.0.11 dev: true - /@vue/reactivity/3.0.7: - resolution: {integrity: sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw==} + /@vue/reactivity/3.0.11: + resolution: {integrity: sha512-SKM3YKxtXHBPMf7yufXeBhCZ4XZDKP9/iXeQSC8bBO3ivBuzAi4aZi0bNoeE2IF2iGfP/AHEt1OU4ARj4ao/Xw==} dependencies: - '@vue/shared': 3.0.7 + '@vue/shared': 3.0.11 dev: true - /@vue/runtime-core/3.0.7: - resolution: {integrity: sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ==} + /@vue/runtime-core/3.0.11: + resolution: {integrity: sha512-87XPNwHfz9JkmOlayBeCCfMh9PT2NBnv795DSbi//C/RaAnc/bGZgECjmkD7oXJ526BZbgk9QZBPdFT8KMxkAg==} dependencies: - '@vue/reactivity': 3.0.7 - '@vue/shared': 3.0.7 + '@vue/reactivity': 3.0.11 + '@vue/shared': 3.0.11 dev: true - /@vue/runtime-dom/3.0.7: - resolution: {integrity: sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g==} + /@vue/runtime-dom/3.0.11: + resolution: {integrity: sha512-jm3FVQESY3y2hKZ2wlkcmFDDyqaPyU3p1IdAX92zTNeCH7I8zZ37PtlE1b9NlCtzV53WjB4TZAYh9yDCMIEumA==} dependencies: - '@vue/runtime-core': 3.0.7 - '@vue/shared': 3.0.7 + '@vue/runtime-core': 3.0.11 + '@vue/shared': 3.0.11 csstype: 2.6.16 dev: true - /@vue/shared/3.0.7: - resolution: {integrity: sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg==} + /@vue/shared/3.0.11: + resolution: {integrity: sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==} dev: true /@zerodevx/svelte-toast/0.2.1: @@ -579,7 +583,7 @@ packages: resolution: {integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==} engines: {node: '>= 0.6'} dependencies: - mime-types: 2.1.29 + mime-types: 2.1.30 negotiator: 0.6.2 dev: true @@ -615,8 +619,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /acorn/8.1.0: - resolution: {integrity: sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==} + /acorn/8.1.1: + resolution: {integrity: sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g==} engines: {node: '>=0.4.0'} hasBin: true @@ -637,6 +641,15 @@ packages: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + /ajv/8.1.0: + resolution: {integrity: sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: false + /ansi-align/3.0.0: resolution: {integrity: sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==} dependencies: @@ -674,12 +687,12 @@ packages: resolution: {integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8=} dev: true - /anymatch/3.1.1: - resolution: {integrity: sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==} + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} engines: {node: '>= 8'} dependencies: normalize-path: 3.0.0 - picomatch: 2.2.2 + picomatch: 2.2.3 dev: true /archy/1.0.0: @@ -774,8 +787,8 @@ packages: resolution: {integrity: sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==} hasBin: true dependencies: - browserslist: 4.16.3 - caniuse-lite: 1.0.30001204 + browserslist: 4.16.4 + caniuse-lite: 1.0.30001209 colorette: 1.2.2 normalize-range: 0.1.2 num2fraction: 1.2.2 @@ -810,8 +823,8 @@ packages: - debug dev: false - /balanced-match/1.0.0: - resolution: {integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=} + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -886,7 +899,7 @@ packages: /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: - balanced-match: 1.0.0 + balanced-match: 1.0.2 concat-map: 0.0.1 /braces/3.0.2: @@ -907,14 +920,14 @@ packages: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} dev: false - /browserslist/4.16.3: - resolution: {integrity: sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==} + /browserslist/4.16.4: + resolution: {integrity: sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001209 colorette: 1.2.2 - electron-to-chromium: 1.3.698 + electron-to-chromium: 1.3.717 escalade: 3.1.1 node-releases: 1.1.71 dev: true @@ -972,7 +985,7 @@ packages: resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} engines: {node: '>= 6.0.0'} dependencies: - mime-types: 2.1.29 + mime-types: 2.1.30 ylru: 1.2.1 dev: true @@ -1016,8 +1029,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001204: - resolution: {integrity: sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==} + /caniuse-lite/1.0.30001209: + resolution: {integrity: sha512-2Ktt4OeRM7EM/JaOZjuLzPYAIqmbwQMNnYbgooT+icoRGrKOyAxA1xhlnotBD1KArRSPsuJp3TdYcZYrL7qNxA==} dev: true /caseless/0.12.0: @@ -1056,7 +1069,7 @@ packages: resolution: {integrity: sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==} engines: {node: '>= 8.10.0'} dependencies: - anymatch: 3.1.1 + anymatch: 3.1.2 braces: 3.0.2 glob-parent: 5.1.2 is-binary-path: 2.1.0 @@ -1383,7 +1396,7 @@ packages: dependencies: abab: 2.0.5 whatwg-mimetype: 2.3.0 - whatwg-url: 8.4.0 + whatwg-url: 8.5.0 dev: false /dayjs/1.10.4: @@ -1465,8 +1478,8 @@ packages: resolution: {integrity: sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=} dev: true - /degit/2.8.0: - resolution: {integrity: sha512-nxQr1Ep4NSGDIqQ3HSMNgnPYaxPfPhHdrpgSNO2EczO86zN7NJJ1i/59GM25vgC45ANQUazZ/3Z+iyeZCmGwhg==} + /degit/2.8.4: + resolution: {integrity: sha512-vqYuzmSA5I50J882jd+AbAhQtgK6bdKUJIex1JNfEUPENCgYsxugzKVZlFyMwV4i06MmnV47/Iqi5Io86zf3Ng==} engines: {node: '>=8.0.0'} hasBin: true dev: true @@ -1615,8 +1628,8 @@ packages: /ee-first/1.1.1: resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} - /electron-to-chromium/1.3.698: - resolution: {integrity: sha512-VEXDzYblnlT+g8Q3gedwzgKOso1evkeJzV8lih7lV8mL8eAnGVnKyC3KsFT6S+R5PQO4ffdr1PI16/ElibY/kQ==} + /electron-to-chromium/1.3.717: + resolution: {integrity: sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ==} dev: true /emoji-regex/7.0.3: @@ -1681,7 +1694,7 @@ packages: object.assign: 4.1.2 string.prototype.trimend: 1.0.4 string.prototype.trimstart: 1.0.4 - unbox-primitive: 1.0.0 + unbox-primitive: 1.0.1 dev: true /es-module-lexer/0.3.26: @@ -2088,15 +2101,15 @@ packages: '@nodelib/fs.walk': 1.2.6 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.2 - picomatch: 2.2.2 + micromatch: 4.0.4 + picomatch: 2.2.3 dev: true /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - /fast-json-stringify/2.5.2: - resolution: {integrity: sha512-H0/Wq7jj/i96DycCySdh+Jl0MubqaScM51GJkdJmjzwyiNpxAgQ9deYqk2H8SNOfwPj21RtTXMkMV5GosUWKeQ==} + /fast-json-stringify/2.5.4: + resolution: {integrity: sha512-fu74X0fRzQqADX6LFJ+5lSal1+j/QmX4oWrDnrfVAXV4qT6PwyymZmhGa/1SWgouOmf0tBJzZrHZPLymO00Lxg==} engines: {node: '>= 10.0.0'} dependencies: ajv: 6.12.6 @@ -2178,16 +2191,16 @@ packages: abstract-logging: 2.0.1 ajv: 6.12.6 avvio: 7.2.1 - fast-json-stringify: 2.5.2 + fast-json-stringify: 2.5.4 fastify-error: 0.3.0 fastify-warning: 0.2.0 - find-my-way: 4.0.0 + find-my-way: 4.1.0 flatstr: 1.0.12 light-my-request: 4.4.1 - pino: 6.11.2 + pino: 6.11.3 readable-stream: 3.6.0 rfdc: 1.3.0 - secure-json-parse: 2.3.2 + secure-json-parse: 2.4.0 semver: 7.3.5 tiny-lru: 7.0.6 transitivePeerDependencies: @@ -2256,8 +2269,8 @@ packages: pkg-dir: 4.2.0 dev: true - /find-my-way/4.0.0: - resolution: {integrity: sha512-IrICzn/Xm5r5A3RCB8rGLNe+dvzZl+SiUugTQOUMLJciP2qiSu2hw9JtVEYV3VruAYzSWjo/MLH9CFKtVdlWhQ==} + /find-my-way/4.1.0: + resolution: {integrity: sha512-UBD94MdO6cBi6E97XA0fBA9nwqw+xG5x1TYIPHats33gEi/kNqy7BWHAWx8QHCQQRSU5Txc0JiD8nzba39gvMQ==} engines: {node: '>=10'} dependencies: fast-decode-uri-component: 1.0.1 @@ -2332,7 +2345,7 @@ packages: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 - mime-types: 2.1.29 + mime-types: 2.1.30 dev: false /forwarded/0.1.2: @@ -2551,8 +2564,8 @@ packages: resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} dev: true - /hosted-git-info/2.8.8: - resolution: {integrity: sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==} + /hosted-git-info/2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true /html-encoding-sniffer/2.0.1: @@ -2579,6 +2592,11 @@ packages: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} dev: true + /http-errors-enhanced/0.7.0: + resolution: {integrity: sha512-SYVGmRIcKBurS7oDTPDnoYxBnEa8CPR0UZBAU6P0QsBPQY6XjIGoCK5iFxLkg1jeo+dkiq4MxqMJbBEoXcczBg==} + engines: {node: '>=12.15.0'} + dev: false + /http-errors/1.4.0: resolution: {integrity: sha1-bAJC3qaz33r9oVPHEImzHG6Cqr8=} engines: {node: '>= 0.6'} @@ -2674,13 +2692,13 @@ packages: resolution: {integrity: sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=} dev: true - /icss-utils/5.1.0_postcss@8.2.8: + /icss-utils/5.1.0_postcss@8.2.10: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.2.8 + postcss: 8.2.10 dev: true /ieee754/1.2.1: @@ -2834,8 +2852,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /is-docker/2.1.1: - resolution: {integrity: sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==} + /is-docker/2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true dev: true @@ -2931,14 +2949,14 @@ packages: engines: {node: '>=8'} dev: true - /is-potential-custom-element-name/1.0.0: - resolution: {integrity: sha1-DFLlS8yjkbssSUsh6GJtczbG45c=} + /is-potential-custom-element-name/1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: false /is-reference/1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} dependencies: - '@types/estree': 0.0.46 + '@types/estree': 0.0.47 dev: true /is-regex/1.1.2: @@ -2978,7 +2996,7 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} dependencies: - is-docker: 2.1.1 + is-docker: 2.2.1 dev: true /is-yarn-global/0.3.0: @@ -3010,7 +3028,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 14.14.35 + '@types/node': 14.14.41 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -3027,8 +3045,8 @@ packages: esprima: 4.0.1 dev: true - /js-yaml/4.0.0: - resolution: {integrity: sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==} + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 @@ -3038,8 +3056,8 @@ packages: resolution: {integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM=} dev: false - /jsdom/16.5.1_c70f8fc5586dd378b8c866035dbe710b: - resolution: {integrity: sha512-pF73EOsJgwZekbDHEY5VO/yKXUkab/DuvrQB/ANVizbr6UAHJsDdHXuotZYwkJSGQl1JM+ivXaqY+XBDDL4TiA==} + /jsdom/16.5.3_c70f8fc5586dd378b8c866035dbe710b: + resolution: {integrity: sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA==} engines: {node: '>=10'} peerDependencies: canvas: ^2.5.0 @@ -3048,7 +3066,7 @@ packages: optional: true dependencies: abab: 2.0.5 - acorn: 8.1.0 + acorn: 8.1.1 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 @@ -3057,7 +3075,7 @@ packages: domexception: 2.0.1 escodegen: 2.0.0 html-encoding-sniffer: 2.0.1 - is-potential-custom-element-name: 1.0.0 + is-potential-custom-element-name: 1.0.1 nwsapi: 2.2.0 parse5: 6.0.1 request: 2.88.2 @@ -3070,7 +3088,7 @@ packages: webidl-conversions: 6.1.0 whatwg-encoding: 1.0.5 whatwg-mimetype: 2.3.0 - whatwg-url: 8.4.0 + whatwg-url: 8.5.0 ws: 7.4.4_c70f8fc5586dd378b8c866035dbe710b xml-name-validator: 3.0.0 transitivePeerDependencies: @@ -3093,6 +3111,10 @@ packages: /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + /json-schema-traverse/1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: false + /json-schema/0.2.3: resolution: {integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=} dev: false @@ -3386,10 +3408,6 @@ packages: signal-exit: 3.0.3 dev: true - /lodash._reinterpolate/3.0.0: - resolution: {integrity: sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=} - dev: true - /lodash.camelcase/4.3.0: resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} dev: true @@ -3422,23 +3440,6 @@ packages: resolution: {integrity: sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=} dev: false - /lodash.sortby/4.7.0: - resolution: {integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=} - dev: false - - /lodash.template/4.5.0: - resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==} - dependencies: - lodash._reinterpolate: 3.0.0 - lodash.templatesettings: 4.2.0 - dev: true - - /lodash.templatesettings/4.2.0: - resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==} - dependencies: - lodash._reinterpolate: 3.0.0 - dev: true - /lodash.toarray/4.4.0: resolution: {integrity: sha1-JMS/zWsvuji/0FlNsRedjptlZWE=} dev: true @@ -3553,23 +3554,23 @@ packages: dev: true optional: true - /micromatch/4.0.2: - resolution: {integrity: sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==} - engines: {node: '>=8'} + /micromatch/4.0.4: + resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} + engines: {node: '>=8.6'} dependencies: braces: 3.0.2 - picomatch: 2.2.2 + picomatch: 2.2.3 dev: true - /mime-db/1.46.0: - resolution: {integrity: sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==} + /mime-db/1.47.0: + resolution: {integrity: sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==} engines: {node: '>= 0.6'} - /mime-types/2.1.29: - resolution: {integrity: sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==} + /mime-types/2.1.30: + resolution: {integrity: sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==} engines: {node: '>= 0.6'} dependencies: - mime-db: 1.46.0 + mime-db: 1.47.0 /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -3643,7 +3644,7 @@ packages: uuid: 8.3.2 yauzl: 2.10.0 optionalDependencies: - mongodb: 3.6.5 + mongodb: 3.6.6 transitivePeerDependencies: - aws4 - bson-ext @@ -3654,8 +3655,8 @@ packages: - supports-color dev: true - /mongodb/3.6.5: - resolution: {integrity: sha512-mQlYKw1iGbvJJejcPuyTaytq0xxlYbIoVDm2FODR+OHxyEiMR021vc32bTvamgBjCswsD54XIRwhg3yBaWqJjg==} + /mongodb/3.6.6: + resolution: {integrity: sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==} engines: {node: '>=4'} peerDependencies: aws4: '*' @@ -3681,28 +3682,28 @@ packages: bl: 2.2.1 bson: 1.1.6 denque: 1.5.0 - require_optional: 1.0.1 + optional-require: 1.0.3 safe-buffer: 5.2.1 optionalDependencies: saslprep: 1.0.3 - /mongoose-legacy-pluralize/1.0.2_mongoose@5.12.3: + /mongoose-legacy-pluralize/1.0.2_mongoose@5.12.4: resolution: {integrity: sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==} peerDependencies: mongoose: '*' dependencies: - mongoose: 5.12.3 + mongoose: 5.12.4 dev: false - /mongoose/5.12.3: - resolution: {integrity: sha512-frsSR9yeldaRpSUeTegXCSB0Tu5UGq8sHuHBuEV31Jk3COyxlKFQPL7UsdMhxPUCmk74FpOYSmNwxhWBEqgzQg==} + /mongoose/5.12.4: + resolution: {integrity: sha512-iVREPLK/35ylEdaNBCStwTugyUYDv7ZuI7maSW7CdCgAX4dMW4be1CdKvZHJtlexO/ugKphMMFL9/bppcWXQ9Q==} engines: {node: '>=4.0.0'} dependencies: - '@types/mongodb': 3.6.10 + '@types/mongodb': 3.6.12 bson: 1.1.6 kareem: 2.3.2 - mongodb: 3.6.5 - mongoose-legacy-pluralize: 1.0.2_mongoose@5.12.3 + mongodb: 3.6.6 + mongoose-legacy-pluralize: 1.0.2_mongoose@5.12.4 mpath: 0.8.3 mquery: 3.2.5 ms: 2.1.2 @@ -3809,12 +3810,12 @@ packages: update-notifier: 4.1.3 dev: true - /nollup/0.15.6: - resolution: {integrity: sha512-FKmFzuI0HXSw9txnuwNwyNi5hgzjqvv+Pk++VdLW5Vo6MXunMcGDdFIpr8xsgrIjAixSwIQVBqrioX3tcj9e1g==} + /nollup/0.16.1: + resolution: {integrity: sha512-G4XtdErjDS9QEw5jK38hLR6W57TwkpiMFvTpkpHunPg3t2wSZwsBTPqENTfNPundAip0GFNefyAvT8LGEtvkkQ==} hasBin: true dependencies: '@rollup/pluginutils': 3.1.0 - acorn: 8.1.0 + acorn: 8.1.1 chokidar: 3.5.1 convert-source-map: 1.7.0 express: 4.17.1 @@ -3822,7 +3823,7 @@ packages: express-http-proxy: 1.6.2 express-ws: 4.0.0_express@4.17.1 magic-string: 0.25.7 - mime-types: 2.1.29 + mime-types: 2.1.30 source-map: 0.5.7 source-map-fast: /source-map/0.7.3 transitivePeerDependencies: @@ -3840,7 +3841,7 @@ packages: /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: - hosted-git-info: 2.8.8 + hosted-git-info: 2.8.9 resolve: 1.20.0 semver: 5.7.1 validate-npm-package-license: 3.0.4 @@ -3981,10 +3982,14 @@ packages: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} dependencies: - is-docker: 2.1.1 + is-docker: 2.2.1 is-wsl: 2.2.0 dev: true + /optional-require/1.0.3: + resolution: {integrity: sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==} + engines: {node: '>=4'} + /optionator/0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -4212,8 +4217,8 @@ packages: resolution: {integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=} dev: false - /picomatch/2.2.2: - resolution: {integrity: sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==} + /picomatch/2.2.3: + resolution: {integrity: sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==} engines: {node: '>=8.6'} /pidtree/0.3.1: @@ -4241,16 +4246,16 @@ packages: resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} dev: false - /pino/6.11.2: - resolution: {integrity: sha512-bmzxwbrIPxQUlAuMkF4PWVErUGERU4z37HazlhflKFg08crsNE3fACGN6gPwg5xtKOK47Ux5cZm8YCuLV4wWJg==} + /pino/6.11.3: + resolution: {integrity: sha512-drPtqkkSf0ufx2gaea3TryFiBHdNIdXKf5LN0hTM82SXI4xVIve2wLwNg92e1MT6m3jASLu6VO7eGY6+mmGeyw==} hasBin: true dependencies: fast-redact: 3.0.0 fast-safe-stringify: 2.0.7 flatstr: 1.0.12 pino-std-serializers: 3.2.0 - quick-format-unescaped: 4.0.1 - sonic-boom: 1.4.0 + quick-format-unescaped: 4.0.3 + sonic-boom: 1.4.1 dev: false /pkg-conf/3.1.0: @@ -4432,22 +4437,21 @@ packages: resolve: 1.20.0 dev: true - /postcss-import/14.0.1_postcss@8.2.9: + /postcss-import/14.0.1_postcss@8.2.10: resolution: {integrity: sha512-Xn2+z++vWObbEPhiiKO1a78JiyhqipyrXHBb3AHpv0ks7Cdg+GxQQJ24ODNMTanldf7197gSP3axppO9yaG0lA==} engines: {node: '>=10.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.2.9 + postcss: 8.2.10 postcss-value-parser: 4.1.0 read-cache: 1.0.0 resolve: 1.20.0 dev: true - /postcss-initial/3.0.2: - resolution: {integrity: sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==} + /postcss-initial/3.0.4: + resolution: {integrity: sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==} dependencies: - lodash.template: 4.5.0 postcss: 7.0.35 dev: true @@ -4456,7 +4460,7 @@ packages: engines: {node: '>=10.0'} dependencies: camelcase-css: 2.0.1 - postcss: 8.2.8 + postcss: 8.2.10 dev: true /postcss-lab-function/2.0.1: @@ -4490,48 +4494,48 @@ packages: postcss: 7.0.35 dev: true - /postcss-modules-extract-imports/3.0.0_postcss@8.2.8: + /postcss-modules-extract-imports/3.0.0_postcss@8.2.10: resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.2.8 + postcss: 8.2.10 dev: true - /postcss-modules-local-by-default/4.0.0_postcss@8.2.8: + /postcss-modules-local-by-default/4.0.0_postcss@8.2.10: resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0_postcss@8.2.8 - postcss: 8.2.8 + icss-utils: 5.1.0_postcss@8.2.10 + postcss: 8.2.10 postcss-selector-parser: 6.0.4 postcss-value-parser: 4.1.0 dev: true - /postcss-modules-scope/3.0.0_postcss@8.2.8: + /postcss-modules-scope/3.0.0_postcss@8.2.10: resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.2.8 + postcss: 8.2.10 postcss-selector-parser: 6.0.4 dev: true - /postcss-modules-values/4.0.0_postcss@8.2.8: + /postcss-modules-values/4.0.0_postcss@8.2.10: resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0_postcss@8.2.8 - postcss: 8.2.8 + icss-utils: 5.1.0_postcss@8.2.10 + postcss: 8.2.10 dev: true - /postcss-modules/4.0.0_postcss@8.2.8: + /postcss-modules/4.0.0_postcss@8.2.10: resolution: {integrity: sha512-ghS/ovDzDqARm4Zj6L2ntadjyQMoyJmi0JkLlYtH2QFLrvNlxH5OAVRPWPeKilB0pY7SbuhO173KOWkPAxRJcw==} peerDependencies: postcss: ^8.0.0 @@ -4539,21 +4543,21 @@ packages: generic-names: 2.0.1 icss-replace-symbols: 1.1.0 lodash.camelcase: 4.3.0 - postcss: 8.2.8 - postcss-modules-extract-imports: 3.0.0_postcss@8.2.8 - postcss-modules-local-by-default: 4.0.0_postcss@8.2.8 - postcss-modules-scope: 3.0.0_postcss@8.2.8 - postcss-modules-values: 4.0.0_postcss@8.2.8 + postcss: 8.2.10 + postcss-modules-extract-imports: 3.0.0_postcss@8.2.10 + postcss-modules-local-by-default: 4.0.0_postcss@8.2.10 + postcss-modules-scope: 3.0.0_postcss@8.2.10 + postcss-modules-values: 4.0.0_postcss@8.2.10 string-hash: 1.1.3 dev: true - /postcss-nested/5.0.5_postcss@8.2.9: + /postcss-nested/5.0.5_postcss@8.2.10: resolution: {integrity: sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==} engines: {node: '>=10.0'} peerDependencies: postcss: ^8.1.13 dependencies: - postcss: 8.2.9 + postcss: 8.2.10 postcss-selector-parser: 6.0.4 dev: true @@ -4590,8 +4594,8 @@ packages: engines: {node: '>=6.0.0'} dependencies: autoprefixer: 9.8.6 - browserslist: 4.16.3 - caniuse-lite: 1.0.30001204 + browserslist: 4.16.4 + caniuse-lite: 1.0.30001209 css-blank-pseudo: 0.1.4 css-has-pseudo: 0.10.0 css-prefers-color-scheme: 3.1.1 @@ -4614,7 +4618,7 @@ packages: postcss-font-variant: 4.0.1 postcss-gap-properties: 2.0.0 postcss-image-set-function: 3.0.1 - postcss-initial: 3.0.2 + postcss-initial: 3.0.4 postcss-lab-function: 2.0.1 postcss-logical: 3.0.0 postcss-media-minmax: 4.0.0 @@ -4645,14 +4649,14 @@ packages: /postcss-selector-matches/4.0.0: resolution: {integrity: sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==} dependencies: - balanced-match: 1.0.0 + balanced-match: 1.0.2 postcss: 7.0.35 dev: true /postcss-selector-not/4.0.1: resolution: {integrity: sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==} dependencies: - balanced-match: 1.0.0 + balanced-match: 1.0.2 postcss: 7.0.35 dev: true @@ -4710,17 +4714,8 @@ packages: supports-color: 6.1.0 dev: true - /postcss/8.2.8: - resolution: {integrity: sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - colorette: 1.2.2 - nanoid: 3.1.22 - source-map: 0.6.1 - dev: true - - /postcss/8.2.9: - resolution: {integrity: sha512-b+TmuIL4jGtCHtoLi+G/PisuIl9avxs8IZMSmlABRwNz5RLUUACrC+ws81dcomz1nRezm5YPdXiMEzBEKgYn+Q==} + /postcss/8.2.10: + resolution: {integrity: sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==} engines: {node: ^10 || ^12 || >=14} dependencies: colorette: 1.2.2 @@ -4820,7 +4815,7 @@ packages: dependencies: commander: 6.2.1 glob: 7.1.6 - postcss: 8.2.8 + postcss: 8.2.10 postcss-selector-parser: 6.0.4 dev: true @@ -4838,8 +4833,8 @@ packages: /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - /quick-format-unescaped/4.0.1: - resolution: {integrity: sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A==} + /quick-format-unescaped/4.0.3: + resolution: {integrity: sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg==} dev: false /quick-lru/5.1.1: @@ -4948,7 +4943,7 @@ packages: resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} engines: {node: '>=8.10.0'} dependencies: - picomatch: 2.2.2 + picomatch: 2.2.3 dev: true /rechoir/0.6.2: @@ -5036,7 +5031,7 @@ packages: is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 - mime-types: 2.1.29 + mime-types: 2.1.30 oauth-sign: 0.9.0 performance-now: 2.1.0 qs: 6.5.2 @@ -5052,24 +5047,19 @@ packages: dev: true optional: true + /require-from-string/2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: false + /require-relative/0.8.7: resolution: {integrity: sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=} dev: true - /require_optional/1.0.1: - resolution: {integrity: sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==} - dependencies: - resolve-from: 2.0.0 - semver: 5.7.1 - /requires-port/1.0.0: resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} dev: true - /resolve-from/2.0.0: - resolution: {integrity: sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=} - engines: {node: '>=0.10.0'} - /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5135,10 +5125,10 @@ packages: glob: 7.1.6 dev: true - /rollup-plugin-dynamic-import-variables/1.1.0_rollup@2.42.3: + /rollup-plugin-dynamic-import-variables/1.1.0_rollup@2.45.2: resolution: {integrity: sha512-C1avEmnXC8cC4aAQ5dB63O9oQf7IrhEHc98bQw9Qd6H36FxtZooLCvVfcO4SNYrqaNrzH3ErucQt/zdFSLPHNw==} dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.42.3 + '@rollup/pluginutils': 3.1.0_rollup@2.45.2 estree-walker: 2.0.2 globby: 11.0.3 magic-string: 0.25.7 @@ -5167,17 +5157,17 @@ packages: svelte: 3.37.0 svelte-hmr: 0.11.6_svelte@3.37.0 optionalDependencies: - nollup: 0.15.6 + nollup: 0.16.1 dev: true - /rollup-plugin-terser/7.0.2_rollup@2.42.3: + /rollup-plugin-terser/7.0.2_rollup@2.45.2: resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} peerDependencies: rollup: ^2.0.0 dependencies: '@babel/code-frame': 7.12.13 jest-worker: 26.6.2 - rollup: 2.42.3 + rollup: 2.45.2 serialize-javascript: 4.0.0 terser: 5.6.1 dev: true @@ -5196,12 +5186,12 @@ packages: dev: true optional: true - /rollup-plugin-vue/6.0.0_@vue+compiler-sfc@3.0.7: + /rollup-plugin-vue/6.0.0_@vue+compiler-sfc@3.0.11: resolution: {integrity: sha512-oVvUd84d5u73M2HYM3XsMDLtZRIA/tw2U0dmHlXU2UWP5JARYHzh/U9vcxaN/x/9MrepY7VH3pHFeOhrWpxs/Q==} peerDependencies: '@vue/compiler-sfc': '*' dependencies: - '@vue/compiler-sfc': 3.0.7_vue@3.0.7 + '@vue/compiler-sfc': 3.0.11_vue@3.0.11 debug: 4.3.1 hash-sum: 2.0.0 rollup-pluginutils: 2.8.2 @@ -5209,12 +5199,12 @@ packages: - supports-color dev: true - /rollup-plugin-web-worker-loader/1.6.1_rollup@2.42.3: + /rollup-plugin-web-worker-loader/1.6.1_rollup@2.45.2: resolution: {integrity: sha512-4QywQSz1NXFHKdyiou16mH3ijpcfLtLGOrAqvAqu1Gx+P8+zj+3gwC2BSL/VW1d+LW4nIHC8F7d7OXhs9UdR2A==} peerDependencies: rollup: ^1.9.2 || ^2.0.0 dependencies: - rollup: 2.42.3 + rollup: 2.45.2 dev: true /rollup-pluginutils/2.8.2: @@ -5222,8 +5212,8 @@ packages: dependencies: estree-walker: 0.6.1 - /rollup/2.42.3: - resolution: {integrity: sha512-JjaT9WaUS5vmjy6xUrnPOskjkQg2cN4WSACNCwbOvBz8VDmbiKVdmTFUoMPRqTud0tsex8Xy9/boLbDW9HKD1w==} + /rollup/2.45.2: + resolution: {integrity: sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==} engines: {node: '>=10.0.0'} hasBin: true optionalDependencies: @@ -5265,8 +5255,8 @@ packages: xmlchars: 2.2.0 dev: false - /secure-json-parse/2.3.2: - resolution: {integrity: sha512-4oUSFU0w2d8/XQb7NO9dbMYyp/hxIwZPcZcGAlAAEziMRHs+NbUcx2Z5dda/z8o+avyQ8gpuYnTMlGh8SVwg9g==} + /secure-json-parse/2.4.0: + resolution: {integrity: sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==} dev: false /selfsigned/1.10.8: @@ -5429,8 +5419,8 @@ packages: resolution: {integrity: sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=} dev: false - /sonic-boom/1.4.0: - resolution: {integrity: sha512-1xUAszhQBOrjk7uisbStQZYkZxD3vkYlCUw5qzOblWQ1ILN5v0dVPAs+QPgszzoPmbdWx6jyT9XiLJ95JdlLiQ==} + /sonic-boom/1.4.1: + resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==} dependencies: atomic-sleep: 1.0.0 flatstr: 1.0.12 @@ -5728,58 +5718,7 @@ packages: svelte: 3.37.0 dev: true - /svelte-preprocess/4.6.9_fa8d64e4f515eee295d8f0f45fceadd2: - resolution: {integrity: sha512-SROWH0rB0DJ+0Ii264cprmNu/NJyZacs5wFD71ya93Cg/oA2lKHgQm4F6j0EWA4ktFMzeuJJm/eX6fka39hEHA==} - engines: {node: '>= 9.11.2'} - requiresBuild: true - peerDependencies: - '@babel/core': ^7.10.2 - coffeescript: ^2.5.1 - less: ^3.11.3 - node-sass: '*' - postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 - pug: ^3.0.0 - sass: ^1.26.8 - stylus: ^0.54.7 - sugarss: ^2.0.0 - svelte: ^3.23.0 - typescript: ^3.9.5 || ^4.0.0 - peerDependenciesMeta: - '@babel/core': - optional: true - coffeescript: - optional: true - less: - optional: true - node-sass: - optional: true - postcss: - optional: true - postcss-load-config: - optional: true - pug: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - typescript: - optional: true - dependencies: - '@types/pug': 2.0.4 - '@types/sass': 1.16.0 - detect-indent: 6.0.0 - postcss: 8.2.9 - postcss-load-config: 3.0.1 - strip-indent: 3.0.0 - svelte: 3.37.0 - dev: true - optional: true - - /svelte-preprocess/4.7.0_fa8d64e4f515eee295d8f0f45fceadd2: + /svelte-preprocess/4.7.0_4a3768216fad352756a1a13607f73a31: resolution: {integrity: sha512-iNrY4YGqi0LD2e6oT9YbdSzOKntxk8gmzfqso1z/lUJOZh4o6fyIqkirmiZ8/dDJFqtIE1spVgDFWgkfhLEYlw==} engines: {node: '>= 9.11.2'} requiresBuild: true @@ -5823,7 +5762,7 @@ packages: '@types/pug': 2.0.4 '@types/sass': 1.16.0 detect-indent: 6.0.0 - postcss: 8.2.9 + postcss: 8.2.10 postcss-load-config: 3.0.1 strip-indent: 3.0.0 svelte: 3.37.0 @@ -5838,7 +5777,7 @@ packages: engines: {node: '>= 8'} dev: true - /svite/0.8.1_d334b093211aa94b4e678204453b11ae: + /svite/0.8.1_79b75b88a1dd913f13bddff6afa5f1de: resolution: {integrity: sha512-DZYF0Icw+omJC8903gfrIWEjfiLyekEDdl08sY+AGrTu0j9k3SHkWkySr+22+GyfRxeY5r/Ag6Ngh6rRULPhGA==} engines: {node: ^12||^14, npm: ^6.14, pnpm: ^5.5, yarn: ^1.22 || ^2} deprecated: svite 0.8.x for vite1 is no longer supported. prerelease for vite2 is 0.9.x, check https://github.com/svitejs/svite/blob/main/packages/svite @@ -5851,7 +5790,7 @@ packages: chalk: 4.1.0 commander: 6.2.1 debug: 4.3.1 - degit: 2.8.0 + degit: 2.8.4 execa: 4.1.0 lru-cache: 6.0.0 rollup-plugin-svelte-hot: 0.11.2_svelte@3.37.0 @@ -5860,7 +5799,7 @@ packages: vite: 1.0.0-rc.13 optionalDependencies: rollup-plugin-visualizer: 4.2.2 - svelte-preprocess: 4.6.9_fa8d64e4f515eee295d8f0f45fceadd2 + svelte-preprocess: 4.7.0_4a3768216fad352756a1a13607f73a31 transitivePeerDependencies: - '@babel/core' - bufferutil @@ -5893,7 +5832,7 @@ packages: string-width: 3.1.0 dev: true - /tailwindcss/2.1.1_postcss@8.2.9: + /tailwindcss/2.1.1_postcss@8.2.10: resolution: {integrity: sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ==} engines: {node: '>=12.13.0'} hasBin: true @@ -5919,10 +5858,10 @@ packages: normalize-path: 3.0.0 object-hash: 2.1.1 parse-glob: 3.0.4 - postcss: 8.2.9 + postcss: 8.2.10 postcss-functions: 3.0.0 postcss-js: 3.0.3 - postcss-nested: 5.0.5_postcss@8.2.9 + postcss-nested: 5.0.5_postcss@8.2.10 postcss-selector-parser: 6.0.4 postcss-value-parser: 4.1.0 pretty-hrtime: 1.0.3 @@ -6086,7 +6025,7 @@ packages: engines: {node: '>= 0.6'} dependencies: media-typer: 0.3.0 - mime-types: 2.1.29 + mime-types: 2.1.30 dev: true /typedarray-to-buffer/3.1.5: @@ -6095,8 +6034,8 @@ packages: is-typedarray: 1.0.0 dev: true - /unbox-primitive/1.0.0: - resolution: {integrity: sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA==} + /unbox-primitive/1.0.1: + resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} dependencies: function-bind: 1.1.1 has-bigints: 1.0.1 @@ -6228,17 +6167,17 @@ packages: engines: {node: '>=10.16.0'} hasBin: true dependencies: - '@babel/parser': 7.13.12 + '@babel/parser': 7.13.15 '@koa/cors': 3.1.0 - '@rollup/plugin-commonjs': 16.0.0_rollup@2.42.3 - '@rollup/plugin-json': 4.1.0_rollup@2.42.3 - '@rollup/plugin-node-resolve': 10.0.0_rollup@2.42.3 - '@rollup/pluginutils': 4.1.0_rollup@2.42.3 + '@rollup/plugin-commonjs': 16.0.0_rollup@2.45.2 + '@rollup/plugin-json': 4.1.0_rollup@2.45.2 + '@rollup/plugin-node-resolve': 10.0.0_rollup@2.45.2 + '@rollup/pluginutils': 4.1.0_rollup@2.45.2 '@types/http-proxy': 1.17.5 '@types/koa': 2.13.1 '@types/lru-cache': 5.1.0 - '@vue/compiler-dom': 3.0.7 - '@vue/compiler-sfc': 3.0.7_vue@3.0.7 + '@vue/compiler-dom': 3.0.11 + '@vue/compiler-sfc': 3.0.11_vue@3.0.11 brotli-size: 4.0.0 cac: 6.7.2 chalk: 4.1.0 @@ -6264,7 +6203,7 @@ packages: lru-cache: 6.0.0 magic-string: 0.25.7 merge-source-map: 1.1.0 - mime-types: 2.1.29 + mime-types: 2.1.30 minimist: 1.2.5 open: 7.4.2 ora: 5.4.0 @@ -6273,15 +6212,15 @@ packages: postcss-import: 12.0.1 postcss-load-config: 3.0.1 resolve: 1.20.0 - rollup: 2.42.3 - rollup-plugin-dynamic-import-variables: 1.1.0_rollup@2.42.3 - rollup-plugin-terser: 7.0.2_rollup@2.42.3 - rollup-plugin-vue: 6.0.0_@vue+compiler-sfc@3.0.7 - rollup-plugin-web-worker-loader: 1.6.1_rollup@2.42.3 + rollup: 2.45.2 + rollup-plugin-dynamic-import-variables: 1.1.0_rollup@2.45.2 + rollup-plugin-terser: 7.0.2_rollup@2.45.2 + rollup-plugin-vue: 6.0.0_@vue+compiler-sfc@3.0.11 + rollup-plugin-web-worker-loader: 1.6.1_rollup@2.45.2 selfsigned: 1.10.8 slash: 3.0.0 source-map: 0.7.3 - vue: 3.0.7 + vue: 3.0.11 ws: 7.4.4 transitivePeerDependencies: - bufferutil @@ -6289,12 +6228,12 @@ packages: - utf-8-validate dev: true - /vue/3.0.7: - resolution: {integrity: sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw==} + /vue/3.0.11: + resolution: {integrity: sha512-3/eUi4InQz8MPzruHYSTQPxtM3LdZ1/S/BvaU021zBnZi0laRUyH6pfuE4wtUeLvI8wmUNwj5wrZFvbHUXL9dw==} dependencies: - '@vue/compiler-dom': 3.0.7 - '@vue/runtime-dom': 3.0.7 - '@vue/shared': 3.0.7 + '@vue/compiler-dom': 3.0.11 + '@vue/runtime-dom': 3.0.11 + '@vue/shared': 3.0.11 dev: true /w3c-hr-time/1.0.2: @@ -6336,11 +6275,11 @@ packages: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} dev: false - /whatwg-url/8.4.0: - resolution: {integrity: sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==} + /whatwg-url/8.5.0: + resolution: {integrity: sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==} engines: {node: '>=10'} dependencies: - lodash.sortby: 4.7.0 + lodash: 4.17.21 tr46: 2.0.2 webidl-conversions: 6.1.0 dev: false @@ -6463,8 +6402,8 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - /y18n/5.0.5: - resolution: {integrity: sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==} + /y18n/5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} dev: true optional: true @@ -6496,7 +6435,7 @@ packages: get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.2 - y18n: 5.0.5 + y18n: 5.0.8 yargs-parser: 20.2.7 dev: true optional: true diff --git a/src/components/Application/Configuration/ActiveTab/General.svelte b/src/components/Application/Configuration/ActiveTab/General.svelte index 915dc8b391..22b62aaf21 100644 --- a/src/components/Application/Configuration/ActiveTab/General.svelte +++ b/src/components/Application/Configuration/ActiveTab/General.svelte @@ -63,7 +63,7 @@
@@ -104,7 +104,7 @@
diff --git a/src/components/Application/Configuration/Configuration.svelte b/src/components/Application/Configuration/Configuration.svelte index 56e8acfa34..c65485af95 100644 --- a/src/components/Application/Configuration/Configuration.svelte +++ b/src/components/Application/Configuration/Configuration.svelte @@ -157,7 +157,7 @@ $application.publish.path}" >{$application.publish.domain ? `${$application.publish.domain}${$application.publish.path !== '/' ? $application.publish.path : ''}` - : "Loading..."}"} f.type === "file" && f.name === "Cargo.toml", ); - if (Dockerfile) { - $application.build.pack = "custom"; - toast.push("Custom Dockerfile found. Build pack set to custom."); - } else if (packageJson) { + if (packageJson) { const { content } = await $fetch(packageJson.git_url); const packageJsonContent = JSON.parse(atob(content)); const checkPackageJSONContents = dep => { @@ -73,17 +70,9 @@ if (checkPackageJSONContents(dep)) { const config = templates[dep]; $application.build.pack = config.pack; - if (config.installation) { - $application.build.command.installation = config.installation; - } - - if (config.port) { - $application.publish.port = config.port; - } - - if (config.directory) { - $application.publish.directory = config.directory; - } + if (config.installation) $application.build.command.installation = config.installation; + if (config.port) $application.publish.port = config.port; + if (config.directory) $application.publish.directory = config.directory; if ( packageJsonContent.scripts.hasOwnProperty("build") && @@ -97,6 +86,9 @@ } else if (CargoToml) { $application.build.pack = "rust"; toast.push(`Rust language detected. Default values set.`); + } else if (Dockerfile) { + $application.build.pack = "custom"; + toast.push("Custom Dockerfile found. Build pack set to custom."); } } catch (error) { // Nothing detected diff --git a/src/index.css b/src/index.css index 9d6645f5ed..2b0db5aea6 100644 --- a/src/index.css +++ b/src/index.css @@ -22,6 +22,11 @@ body { border-image: linear-gradient(0.25turn, rgba(255, 249, 34), rgba(255, 0, 128), rgba(56, 2, 155, 0)); border-image-slice: 1; } +.border-gradient-full { + border: 4px solid transparent; + border-image: linear-gradient(0.25turn, rgba(255, 249, 34), rgba(255, 0, 128), rgba(56, 2, 155, 0)); + border-image-slice: 1; +} [aria-label][role~="tooltip"]::after { background: rgba(41, 37, 36, 0.9); diff --git a/src/pages/_layout.svelte b/src/pages/_layout.svelte index 628224ac10..f354764a43 100644 --- a/src/pages/_layout.svelte +++ b/src/pages/_layout.svelte @@ -17,9 +17,37 @@ let upgradeDisabled = false; let upgradeDone = false; let latest = {}; + let showAck = false; + const branch = + process.env.NODE_ENV === "production" && + window.location.hostname !== "test.andrasbacsai.dev" + ? "main" + : "next"; onMount(async () => { - if ($session.token) upgradeAvailable = await checkUpgrade(); + if ($session.token) { + upgradeAvailable = await checkUpgrade(); + if (!localStorage.getItem("automaticErrorReportsAck")) { + showAck = true; + if (latest?.coolify[branch]?.settings?.sendErrors) { + const settings = { + sendErrors: true, + }; + await $fetch("/api/v1/settings", { + body: { + ...settings, + }, + headers: { + Authorization: `Bearer ${$session.token}`, + }, + }); + } + } + } }); + function ackError() { + localStorage.setItem("automaticErrorReportsAck", "true"); + showAck = false; + } async function verifyToken() { if ($session.token) { try { @@ -69,11 +97,6 @@ cache: "no-cache", }) .then(r => r.json()); - const branch = - process.env.NODE_ENV === "production" && - window.location.hostname !== "test.andrasbacsai.dev" - ? "main" - : "next"; return compareVersions( latest.coolify[branch].version, @@ -85,6 +108,31 @@ {#await verifyToken() then notUsed} + {#if showAck} +
+
+
+ We implemented an automatic error reporting feature, which is enabled + by default. +
+
+ Why? Because we would like to hunt down bugs faster and easier. +
+
+ If you do not like it, you can turn it off in the . +
+ +
+
+ {/if} {#if $route.path !== "/index"}