From 01e71958b28a03ee5e5342381733f12f874d9bd9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 25 Aug 2022 10:04:46 +0200 Subject: [PATCH] fix: build queue system --- apps/api/package.json | 10 +- .../migration.sql | 29 + .../migration.sql | 24 + apps/api/prisma/schema.prisma | 4 + apps/api/src/index.ts | 31 +- apps/api/src/jobs/cleanupStorage.ts | 4 +- apps/api/src/jobs/deployApplication-old.ts | 366 +++++++++ apps/api/src/jobs/deployApplication.ts | 694 +++++++++--------- apps/api/src/lib/buildPacks/common.ts | 2 +- apps/api/src/lib/common.ts | 17 +- apps/api/src/lib/scheduler.ts | 20 +- .../routes/api/v1/applications/handlers.ts | 21 +- .../src/routes/webhooks/github/handlers.ts | 17 +- .../src/routes/webhooks/gitlab/handlers.ts | 16 +- apps/ui/package.json | 12 +- .../routes/applications/[id]/__layout.svelte | 13 +- .../applications/[id]/logs/build.svelte | 2 +- pnpm-lock.yaml | 386 +++------- 18 files changed, 915 insertions(+), 753 deletions(-) create mode 100644 apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql create mode 100644 apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql create mode 100644 apps/api/src/jobs/deployApplication-old.ts diff --git a/apps/api/package.json b/apps/api/package.json index b5dcb5823..f9766c5b5 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -27,7 +27,7 @@ "bcryptjs": "2.4.3", "bree": "9.1.2", "cabin": "9.1.2", - "compare-versions": "4.1.3", + "compare-versions": "4.1.4", "cuid": "2.1.8", "dayjs": "1.11.5", "dockerode": "3.3.4", @@ -43,17 +43,17 @@ "jsonwebtoken": "8.5.1", "node-forge": "1.3.1", "node-os-utils": "1.3.7", - "p-queue": "7.3.0", + "p-all": "4.0.0", "public-ip": "6.0.1", "ssh-config": "4.1.6", "strip-ansi": "7.0.1", "unique-names-generator": "4.7.1" }, "devDependencies": { - "@types/node": "18.7.11", + "@types/node": "18.7.13", "@types/node-os-utils": "1.3.0", - "@typescript-eslint/eslint-plugin": "5.34.0", - "@typescript-eslint/parser": "5.34.0", + "@typescript-eslint/eslint-plugin": "5.35.1", + "@typescript-eslint/parser": "5.35.1", "esbuild": "0.15.5", "eslint": "8.22.0", "eslint-config-prettier": "8.5.0", diff --git a/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql b/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql new file mode 100644 index 000000000..1535c2bf7 --- /dev/null +++ b/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql @@ -0,0 +1,29 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "DNSServers" TEXT, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1 +); +INSERT INTO "new_Setting" ("DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql b/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql new file mode 100644 index 000000000..78c51fc15 --- /dev/null +++ b/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql @@ -0,0 +1,24 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Build" ( + "id" TEXT NOT NULL PRIMARY KEY, + "type" TEXT NOT NULL, + "applicationId" TEXT, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + "commit" TEXT, + "pullmergeRequestId" TEXT, + "forceRebuild" BOOLEAN NOT NULL DEFAULT false, + "sourceBranch" TEXT, + "branch" TEXT, + "status" TEXT DEFAULT 'queued', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Build" ("applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt") SELECT "applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt" FROM "Build"; +DROP TABLE "Build"; +ALTER TABLE "new_Build" RENAME TO "Build"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 02440300b..dfa7ae26b 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -27,6 +27,7 @@ model Setting { ipv4 String? ipv6 String? arch String? + concurrentBuilds Int @default(1) } model User { @@ -197,6 +198,9 @@ model Build { githubAppId String? gitlabAppId String? commit String? + pullmergeRequestId String? + forceRebuild Boolean @default(false) + sourceBranch String? branch String? status String? @default("queued") createdAt DateTime @default(now()) diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 18864dd6e..3f4876c94 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -5,7 +5,7 @@ import env from '@fastify/env'; import cookie from '@fastify/cookie'; import path, { join } from 'path'; import autoLoad from '@fastify/autoload'; -import { asyncExecShell, isDev, listSettings, prisma, version } from './lib/common'; +import { asyncExecShell, asyncSleep, isDev, listSettings, prisma, version } from './lib/common'; import { scheduler } from './lib/scheduler'; import axios from 'axios'; import compareVersions from 'compare-versions'; @@ -104,14 +104,16 @@ fastify.listen({ port, host }, async (err: any, address: any) => { } console.log(`Coolify's API is listening on ${host}:${port}`); await initServer(); - await scheduler.start('deployApplication'); - await scheduler.start('cleanupStorage'); await scheduler.start('cleanupPrismaEngines'); await scheduler.start('checkProxies'); - // Check if no build is running + setInterval(async () => { + if (!scheduler.workers.has('deployApplication')) { + scheduler.run('deployApplication'); + } + }, 2000) - // Check for update + // Check for update & if no build is running setInterval(async () => { const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); if (isAutoUpdateEnabled) { @@ -128,8 +130,8 @@ fastify.listen({ port, host }, async (err: any, address: any) => { const latestVersion = versions['coolify'].main.version; const isUpdateAvailable = compareVersions(latestVersion, currentVersion); if (isUpdateAvailable === 1) { - if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage("status:autoUpdater"); + if (!scheduler.workers.has('deployApplication')) { + await scheduler.run('autoUpdater') } } } @@ -137,16 +139,11 @@ fastify.listen({ port, host }, async (err: any, address: any) => { // Cleanup storage setInterval(async () => { - if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage("status:cleanupStorage"); + if (!scheduler.workers.has('deployApplication') && !scheduler.workers.has('cleanupStorage')) { + await scheduler.run('cleanupStorage') } }, isDev ? 5000 : 60000 * 10) - scheduler.on('worker deleted', async (name) => { - if (name === 'autoUpdater' || name === 'cleanupStorage') { - if (!scheduler.workers.has('deployApplication')) await scheduler.start('deployApplication'); - } - }); await getArch(); await getIPAddress(); }); @@ -170,6 +167,12 @@ async function initServer() { try { await asyncExecShell(`docker network create --attachable coolify`); } catch (error) { } + try { + const isOlder = compareVersions('3.8.1', version); + if (isOlder === -1) { + await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } }); + } + } catch (error) { } } async function getArch() { try { diff --git a/apps/api/src/jobs/cleanupStorage.ts b/apps/api/src/jobs/cleanupStorage.ts index 97683ac2d..4740c1df8 100644 --- a/apps/api/src/jobs/cleanupStorage.ts +++ b/apps/api/src/jobs/cleanupStorage.ts @@ -1,5 +1,5 @@ import { parentPort } from 'node:worker_threads'; -import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, version } from '../lib/common'; +import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma } from '../lib/common'; (async () => { if (parentPort) { @@ -9,7 +9,7 @@ import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return if (destination.engine) enginesDone.add(destination.engine) if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress) - + let lowDiskSpace = false; try { let stdout = null diff --git a/apps/api/src/jobs/deployApplication-old.ts b/apps/api/src/jobs/deployApplication-old.ts new file mode 100644 index 000000000..93d358fe3 --- /dev/null +++ b/apps/api/src/jobs/deployApplication-old.ts @@ -0,0 +1,366 @@ +import { parentPort } from 'node:worker_threads'; +import crypto from 'crypto'; +import fs from 'fs/promises'; +import yaml from 'js-yaml'; + +import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common'; +import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma } from '../lib/common'; +import * as importers from '../lib/importers'; +import * as buildpacks from '../lib/buildPacks'; + +(async () => { + if (parentPort) { + const concurrency = 1 + const PQueue = await import('p-queue'); + const queue = new PQueue.default({ concurrency }); + parentPort.on('message', async (message) => { + if (parentPort) { + if (message === 'error') throw new Error('oops'); + if (message === 'cancel') { + parentPort.postMessage('cancelled'); + return; + } + if (message === 'status:autoUpdater') { + parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'autoUpdater' }); + return; + } + if (message === 'status:cleanupStorage') { + parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'cleanupStorage' }); + return; + } + if (message === 'action:flushQueue') { + queue.clear() + return; + } + + await queue.add(async () => { + const { + id: applicationId, + repository, + name, + destinationDocker, + destinationDockerId, + gitSource, + build_id: buildId, + configHash, + fqdn, + projectId, + secrets, + phpModules, + type, + pullmergeRequestId = null, + sourceBranch = null, + settings, + persistentStorage, + pythonWSGI, + pythonModule, + pythonVariable, + denoOptions, + exposePort, + baseImage, + baseBuildImage, + deploymentType, + forceRebuild + } = message + let { + branch, + buildPack, + port, + installCommand, + buildCommand, + startCommand, + baseDirectory, + publishDirectory, + dockerFileLocation, + denoMainFile + } = message + const currentHash = crypto + .createHash('sha256') + .update( + JSON.stringify({ + pythonWSGI, + pythonModule, + pythonVariable, + deploymentType, + denoOptions, + baseImage, + baseBuildImage, + buildPack, + port, + exposePort, + installCommand, + buildCommand, + startCommand, + secrets, + branch, + repository, + fqdn + }) + ) + .digest('hex'); + try { + const { debug } = settings; + if (concurrency === 1) { + await prisma.build.updateMany({ + where: { + status: { in: ['queued', 'running'] }, + id: { not: buildId }, + applicationId, + createdAt: { lt: new Date(new Date().getTime() - 10 * 1000) } + }, + data: { status: 'failed' } + }); + } + let imageId = applicationId; + let domain = getDomain(fqdn); + const volumes = + persistentStorage?.map((storage) => { + return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : '' + }${storage.path}`; + }) || []; + // Previews, we need to get the source branch and set subdomain + if (pullmergeRequestId) { + branch = sourceBranch; + domain = `${pullmergeRequestId}.${domain}`; + imageId = `${applicationId}-${pullmergeRequestId}`; + } + + let deployNeeded = true; + let destinationType; + + if (destinationDockerId) { + destinationType = 'docker'; + } + if (destinationType === 'docker') { + await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } }); + const { workdir, repodir } = await createDirectories({ repository, buildId }); + const configuration = await setDefaultConfiguration(message); + + buildPack = configuration.buildPack; + port = configuration.port; + installCommand = configuration.installCommand; + startCommand = configuration.startCommand; + buildCommand = configuration.buildCommand; + publishDirectory = configuration.publishDirectory; + baseDirectory = configuration.baseDirectory; + dockerFileLocation = configuration.dockerFileLocation; + denoMainFile = configuration.denoMainFile; + const commit = await importers[gitSource.type]({ + applicationId, + debug, + workdir, + repodir, + githubAppId: gitSource.githubApp?.id, + gitlabAppId: gitSource.gitlabApp?.id, + customPort: gitSource.customPort, + repository, + branch, + buildId, + apiUrl: gitSource.apiUrl, + htmlUrl: gitSource.htmlUrl, + projectId, + deployKeyId: gitSource.gitlabApp?.deployKeyId || null, + privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null, + forPublic: gitSource.forPublic + }); + if (!commit) { + throw new Error('No commit found?'); + } + let tag = commit.slice(0, 7); + if (pullmergeRequestId) { + tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`; + } + + try { + await prisma.build.update({ where: { id: buildId }, data: { commit } }); + } catch (err) { + console.log(err); + } + + if (!pullmergeRequestId) { + + if (configHash !== currentHash) { + deployNeeded = true; + if (configHash) { + await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId }); + } + } else { + deployNeeded = false; + } + } else { + deployNeeded = true; + } + + let imageFound = false; + try { + await executeDockerCmd({ + dockerId: destinationDocker.id, + command: `docker image inspect ${applicationId}:${tag}` + }) + imageFound = true; + } catch (error) { + // + } + await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage); + + if (forceRebuild) deployNeeded = true + if (!imageFound || deployNeeded) { + // if (true) { + if (buildpacks[buildPack]) + await buildpacks[buildPack]({ + dockerId: destinationDocker.id, + buildId, + applicationId, + domain, + name, + type, + pullmergeRequestId, + buildPack, + repository, + branch, + projectId, + publishDirectory, + debug, + commit, + tag, + workdir, + port: exposePort ? `${exposePort}:${port}` : port, + installCommand, + buildCommand, + startCommand, + baseDirectory, + secrets, + phpModules, + pythonWSGI, + pythonModule, + pythonVariable, + dockerFileLocation, + denoMainFile, + denoOptions, + baseImage, + baseBuildImage, + deploymentType + }); + else { + await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); + throw new Error(`Build pack ${buildPack} not found.`); + } + } else { + await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId }); + } + try { + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker stop -t 0 ${imageId}` }) + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker rm ${imageId}` }) + } catch (error) { + // + } + const envs = [ + `PORT=${port}` + ]; + if (secrets.length > 0) { + secrets.forEach((secret) => { + if (pullmergeRequestId) { + if (secret.isPRMRSecret) { + envs.push(`${secret.name}=${secret.value}`); + } + } else { + if (!secret.isPRMRSecret) { + envs.push(`${secret.name}=${secret.value}`); + } + } + }); + } + await fs.writeFile(`${workdir}/.env`, envs.join('\n')); + const labels = makeLabelForStandaloneApplication({ + applicationId, + fqdn, + name, + type, + pullmergeRequestId, + buildPack, + repository, + branch, + projectId, + port: exposePort ? `${exposePort}:${port}` : port, + commit, + installCommand, + buildCommand, + startCommand, + baseDirectory, + publishDirectory + }); + let envFound = false; + try { + envFound = !!(await fs.stat(`${workdir}/.env`)); + } catch (error) { + // + } + try { + await saveBuildLog({ line: 'Deployment started.', buildId, applicationId }); + const composeVolumes = volumes.map((volume) => { + return { + [`${volume.split(':')[0]}`]: { + name: volume.split(':')[0] + } + }; + }); + const composeFile = { + version: '3.8', + services: { + [imageId]: { + image: `${applicationId}:${tag}`, + container_name: imageId, + volumes, + env_file: envFound ? [`${workdir}/.env`] : [], + labels, + depends_on: [], + expose: [port], + ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), + // logging: { + // driver: 'fluentd', + // }, + ...defaultComposeConfiguration(destinationDocker.network), + } + }, + networks: { + [destinationDocker.network]: { + external: true + } + }, + volumes: Object.assign({}, ...composeVolumes) + }; + await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` }) + await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId }); + } catch (error) { + await saveBuildLog({ line: error, buildId, applicationId }); + await prisma.build.updateMany({ + where: { id: message.build_id, status: { in: ['queued', 'running'] } }, + data: { status: 'failed' } + }); + throw new Error(error); + } + await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId }); + await prisma.build.update({ where: { id: message.build_id }, data: { status: 'success' } }); + if (!pullmergeRequestId) await prisma.application.update({ + where: { id: applicationId }, + data: { configHash: currentHash } + }); + } + + } + catch (error) { + await prisma.build.updateMany({ + where: { id: message.build_id, status: { in: ['queued', 'running'] } }, + data: { status: 'failed' } + }); + await saveBuildLog({ line: error, buildId, applicationId }); + } finally { + await prisma.$disconnect(); + } + }); + await prisma.$disconnect(); + } + }); + } else process.exit(0); +})(); diff --git a/apps/api/src/jobs/deployApplication.ts b/apps/api/src/jobs/deployApplication.ts index 93d358fe3..261f8bd32 100644 --- a/apps/api/src/jobs/deployApplication.ts +++ b/apps/api/src/jobs/deployApplication.ts @@ -10,357 +10,353 @@ import * as buildpacks from '../lib/buildPacks'; (async () => { if (parentPort) { - const concurrency = 1 - const PQueue = await import('p-queue'); - const queue = new PQueue.default({ concurrency }); parentPort.on('message', async (message) => { - if (parentPort) { - if (message === 'error') throw new Error('oops'); - if (message === 'cancel') { - parentPort.postMessage('cancelled'); - return; - } - if (message === 'status:autoUpdater') { - parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'autoUpdater' }); - return; - } - if (message === 'status:cleanupStorage') { - parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'cleanupStorage' }); - return; - } - if (message === 'action:flushQueue') { - queue.clear() - return; - } - - await queue.add(async () => { - const { - id: applicationId, - repository, - name, - destinationDocker, - destinationDockerId, - gitSource, - build_id: buildId, - configHash, - fqdn, - projectId, - secrets, - phpModules, - type, - pullmergeRequestId = null, - sourceBranch = null, - settings, - persistentStorage, - pythonWSGI, - pythonModule, - pythonVariable, - denoOptions, - exposePort, - baseImage, - baseBuildImage, - deploymentType, - forceRebuild - } = message - let { - branch, - buildPack, - port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory, - dockerFileLocation, - denoMainFile - } = message - const currentHash = crypto - .createHash('sha256') - .update( - JSON.stringify({ - pythonWSGI, - pythonModule, - pythonVariable, - deploymentType, - denoOptions, - baseImage, - baseBuildImage, - buildPack, - port, - exposePort, - installCommand, - buildCommand, - startCommand, - secrets, - branch, - repository, - fqdn - }) - ) - .digest('hex'); - try { - const { debug } = settings; - if (concurrency === 1) { - await prisma.build.updateMany({ - where: { - status: { in: ['queued', 'running'] }, - id: { not: buildId }, - applicationId, - createdAt: { lt: new Date(new Date().getTime() - 10 * 1000) } - }, - data: { status: 'failed' } - }); - } - let imageId = applicationId; - let domain = getDomain(fqdn); - const volumes = - persistentStorage?.map((storage) => { - return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : '' - }${storage.path}`; - }) || []; - // Previews, we need to get the source branch and set subdomain - if (pullmergeRequestId) { - branch = sourceBranch; - domain = `${pullmergeRequestId}.${domain}`; - imageId = `${applicationId}-${pullmergeRequestId}`; - } - - let deployNeeded = true; - let destinationType; - - if (destinationDockerId) { - destinationType = 'docker'; - } - if (destinationType === 'docker') { - await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } }); - const { workdir, repodir } = await createDirectories({ repository, buildId }); - const configuration = await setDefaultConfiguration(message); - - buildPack = configuration.buildPack; - port = configuration.port; - installCommand = configuration.installCommand; - startCommand = configuration.startCommand; - buildCommand = configuration.buildCommand; - publishDirectory = configuration.publishDirectory; - baseDirectory = configuration.baseDirectory; - dockerFileLocation = configuration.dockerFileLocation; - denoMainFile = configuration.denoMainFile; - const commit = await importers[gitSource.type]({ - applicationId, - debug, - workdir, - repodir, - githubAppId: gitSource.githubApp?.id, - gitlabAppId: gitSource.gitlabApp?.id, - customPort: gitSource.customPort, - repository, - branch, - buildId, - apiUrl: gitSource.apiUrl, - htmlUrl: gitSource.htmlUrl, - projectId, - deployKeyId: gitSource.gitlabApp?.deployKeyId || null, - privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null, - forPublic: gitSource.forPublic - }); - if (!commit) { - throw new Error('No commit found?'); - } - let tag = commit.slice(0, 7); - if (pullmergeRequestId) { - tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`; - } - - try { - await prisma.build.update({ where: { id: buildId }, data: { commit } }); - } catch (err) { - console.log(err); - } - - if (!pullmergeRequestId) { - - if (configHash !== currentHash) { - deployNeeded = true; - if (configHash) { - await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId }); - } - } else { - deployNeeded = false; - } - } else { - deployNeeded = true; - } - - let imageFound = false; - try { - await executeDockerCmd({ - dockerId: destinationDocker.id, - command: `docker image inspect ${applicationId}:${tag}` - }) - imageFound = true; - } catch (error) { - // - } - await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage); - - if (forceRebuild) deployNeeded = true - if (!imageFound || deployNeeded) { - // if (true) { - if (buildpacks[buildPack]) - await buildpacks[buildPack]({ - dockerId: destinationDocker.id, - buildId, - applicationId, - domain, - name, - type, - pullmergeRequestId, - buildPack, - repository, - branch, - projectId, - publishDirectory, - debug, - commit, - tag, - workdir, - port: exposePort ? `${exposePort}:${port}` : port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - secrets, - phpModules, - pythonWSGI, - pythonModule, - pythonVariable, - dockerFileLocation, - denoMainFile, - denoOptions, - baseImage, - baseBuildImage, - deploymentType - }); - else { - await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); - throw new Error(`Build pack ${buildPack} not found.`); - } - } else { - await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId }); - } - try { - await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker stop -t 0 ${imageId}` }) - await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker rm ${imageId}` }) - } catch (error) { - // - } - const envs = [ - `PORT=${port}` - ]; - if (secrets.length > 0) { - secrets.forEach((secret) => { - if (pullmergeRequestId) { - if (secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); - } - } else { - if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); - } - } - }); - } - await fs.writeFile(`${workdir}/.env`, envs.join('\n')); - const labels = makeLabelForStandaloneApplication({ - applicationId, - fqdn, - name, - type, - pullmergeRequestId, - buildPack, - repository, - branch, - projectId, - port: exposePort ? `${exposePort}:${port}` : port, - commit, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory - }); - let envFound = false; - try { - envFound = !!(await fs.stat(`${workdir}/.env`)); - } catch (error) { - // - } - try { - await saveBuildLog({ line: 'Deployment started.', buildId, applicationId }); - const composeVolumes = volumes.map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - }); - const composeFile = { - version: '3.8', - services: { - [imageId]: { - image: `${applicationId}:${tag}`, - container_name: imageId, - volumes, - env_file: envFound ? [`${workdir}/.env`] : [], - labels, - depends_on: [], - expose: [port], - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - // logging: { - // driver: 'fluentd', - // }, - ...defaultComposeConfiguration(destinationDocker.network), - } - }, - networks: { - [destinationDocker.network]: { - external: true - } - }, - volumes: Object.assign({}, ...composeVolumes) - }; - await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); - await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` }) - await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId }); - } catch (error) { - await saveBuildLog({ line: error, buildId, applicationId }); - await prisma.build.updateMany({ - where: { id: message.build_id, status: { in: ['queued', 'running'] } }, - data: { status: 'failed' } - }); - throw new Error(error); - } - await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId }); - await prisma.build.update({ where: { id: message.build_id }, data: { status: 'success' } }); - if (!pullmergeRequestId) await prisma.application.update({ - where: { id: applicationId }, - data: { configHash: currentHash } - }); - } - - } - catch (error) { - await prisma.build.updateMany({ - where: { id: message.build_id, status: { in: ['queued', 'running'] } }, - data: { status: 'failed' } - }); - await saveBuildLog({ line: error, buildId, applicationId }); - } finally { - await prisma.$disconnect(); - } - }); - await prisma.$disconnect(); + if (message === 'error') throw new Error('oops'); + if (message === 'cancel') { + parentPort.postMessage('cancelled'); + process.exit(0); } }); + try { + parentPort.postMessage({ deploying: true }); + const queuedBuilds = await prisma.build.findMany({ where: { status: 'queued' }, orderBy: { createdAt: 'asc' } }); + const { concurrentBuilds } = await prisma.setting.findFirst({}) + if (queuedBuilds.length > 0) { + const concurrency = concurrentBuilds; + const pAll = await import('p-all'); + const actions = [] + + for (const queueBuild of queuedBuilds) { + actions.push(async () => { + const application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } }) + const { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, forceRebuild } = queueBuild + const { + id: applicationId, + repository, + name, + destinationDocker, + destinationDockerId, + gitSource, + configHash, + fqdn, + projectId, + secrets, + phpModules, + settings, + persistentStorage, + pythonWSGI, + pythonModule, + pythonVariable, + denoOptions, + exposePort, + baseImage, + baseBuildImage, + deploymentType, + } = application + let { + branch, + buildPack, + port, + installCommand, + buildCommand, + startCommand, + baseDirectory, + publishDirectory, + dockerFileLocation, + denoMainFile + } = application + const currentHash = crypto + .createHash('sha256') + .update( + JSON.stringify({ + pythonWSGI, + pythonModule, + pythonVariable, + deploymentType, + denoOptions, + baseImage, + baseBuildImage, + buildPack, + port, + exposePort, + installCommand, + buildCommand, + startCommand, + secrets, + branch, + repository, + fqdn + }) + ) + .digest('hex'); + try { + const { debug } = settings; + if (concurrency === 1) { + await prisma.build.updateMany({ + where: { + status: { in: ['queued', 'running'] }, + id: { not: buildId }, + applicationId, + createdAt: { lt: new Date(new Date().getTime() - 10 * 1000) } + }, + data: { status: 'failed' } + }); + } + let imageId = applicationId; + let domain = getDomain(fqdn); + const volumes = + persistentStorage?.map((storage) => { + return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : '' + }${storage.path}`; + }) || []; + // Previews, we need to get the source branch and set subdomain + if (pullmergeRequestId) { + branch = sourceBranch; + domain = `${pullmergeRequestId}.${domain}`; + imageId = `${applicationId}-${pullmergeRequestId}`; + } + + let deployNeeded = true; + let destinationType; + + if (destinationDockerId) { + destinationType = 'docker'; + } + if (destinationType === 'docker') { + await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } }); + const { workdir, repodir } = await createDirectories({ repository, buildId }); + const configuration = await setDefaultConfiguration(application); + + buildPack = configuration.buildPack; + port = configuration.port; + installCommand = configuration.installCommand; + startCommand = configuration.startCommand; + buildCommand = configuration.buildCommand; + publishDirectory = configuration.publishDirectory; + baseDirectory = configuration.baseDirectory; + dockerFileLocation = configuration.dockerFileLocation; + denoMainFile = configuration.denoMainFile; + const commit = await importers[gitSource.type]({ + applicationId, + debug, + workdir, + repodir, + githubAppId: gitSource.githubApp?.id, + gitlabAppId: gitSource.gitlabApp?.id, + customPort: gitSource.customPort, + repository, + branch, + buildId, + apiUrl: gitSource.apiUrl, + htmlUrl: gitSource.htmlUrl, + projectId, + deployKeyId: gitSource.gitlabApp?.deployKeyId || null, + privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null, + forPublic: gitSource.forPublic + }); + if (!commit) { + throw new Error('No commit found?'); + } + let tag = commit.slice(0, 7); + if (pullmergeRequestId) { + tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`; + } + + try { + await prisma.build.update({ where: { id: buildId }, data: { commit } }); + } catch (err) { + console.log(err); + } + + if (!pullmergeRequestId) { + if (configHash !== currentHash) { + deployNeeded = true; + if (configHash) { + await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId }); + } + } else { + deployNeeded = false; + } + } else { + deployNeeded = true; + } + + let imageFound = false; + try { + await executeDockerCmd({ + dockerId: destinationDocker.id, + command: `docker image inspect ${applicationId}:${tag}` + }) + imageFound = true; + } catch (error) { + // + } + await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage); + + if (forceRebuild) deployNeeded = true + if (!imageFound || deployNeeded) { + // if (true) { + if (buildpacks[buildPack]) + await buildpacks[buildPack]({ + dockerId: destinationDocker.id, + buildId, + applicationId, + domain, + name, + type, + pullmergeRequestId, + buildPack, + repository, + branch, + projectId, + publishDirectory, + debug, + commit, + tag, + workdir, + port: exposePort ? `${exposePort}:${port}` : port, + installCommand, + buildCommand, + startCommand, + baseDirectory, + secrets, + phpModules, + pythonWSGI, + pythonModule, + pythonVariable, + dockerFileLocation, + denoMainFile, + denoOptions, + baseImage, + baseBuildImage, + deploymentType + }); + else { + await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); + throw new Error(`Build pack ${buildPack} not found.`); + } + } else { + await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId }); + } + try { + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker stop -t 0 ${imageId}` }) + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker rm ${imageId}` }) + } catch (error) { + // + } + const envs = [ + `PORT=${port}` + ]; + if (secrets.length > 0) { + secrets.forEach((secret) => { + if (pullmergeRequestId) { + if (secret.isPRMRSecret) { + envs.push(`${secret.name}=${secret.value}`); + } + } else { + if (!secret.isPRMRSecret) { + envs.push(`${secret.name}=${secret.value}`); + } + } + }); + } + await fs.writeFile(`${workdir}/.env`, envs.join('\n')); + const labels = makeLabelForStandaloneApplication({ + applicationId, + fqdn, + name, + type, + pullmergeRequestId, + buildPack, + repository, + branch, + projectId, + port: exposePort ? `${exposePort}:${port}` : port, + commit, + installCommand, + buildCommand, + startCommand, + baseDirectory, + publishDirectory + }); + let envFound = false; + try { + envFound = !!(await fs.stat(`${workdir}/.env`)); + } catch (error) { + // + } + try { + await saveBuildLog({ line: 'Deployment started.', buildId, applicationId }); + const composeVolumes = volumes.map((volume) => { + return { + [`${volume.split(':')[0]}`]: { + name: volume.split(':')[0] + } + }; + }); + const composeFile = { + version: '3.8', + services: { + [imageId]: { + image: `${applicationId}:${tag}`, + container_name: imageId, + volumes, + env_file: envFound ? [`${workdir}/.env`] : [], + labels, + depends_on: [], + expose: [port], + ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), + // logging: { + // driver: 'fluentd', + // }, + ...defaultComposeConfiguration(destinationDocker.network), + } + }, + networks: { + [destinationDocker.network]: { + external: true + } + }, + volumes: Object.assign({}, ...composeVolumes) + }; + await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); + await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` }) + await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId }); + } catch (error) { + await saveBuildLog({ line: error, buildId, applicationId }); + await prisma.build.updateMany({ + where: { id: buildId, status: { in: ['queued', 'running'] } }, + data: { status: 'failed' } + }); + throw new Error(error); + } + await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId }); + await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); + if (!pullmergeRequestId) await prisma.application.update({ + where: { id: applicationId }, + data: { configHash: currentHash } + }); + } + } + catch (error) { + await prisma.build.updateMany({ + where: { id: buildId, status: { in: ['queued', 'running'] } }, + data: { status: 'failed' } + }); + await saveBuildLog({ line: error, buildId, applicationId }); + } + }); + } + + await pAll.default(actions, { concurrency }) + } + + } catch (error) { + process.exit(0); + + } finally { + await prisma.$disconnect(); + process.exit(0); + } } else process.exit(0); })(); diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts index b463a91db..c66477a0b 100644 --- a/apps/api/src/lib/buildPacks/common.ts +++ b/apps/api/src/lib/buildPacks/common.ts @@ -553,7 +553,7 @@ export async function buildImage({ await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker build --progress plain -f ${workdir}/${dockerFile} -t ${cache} ${workdir}` }) const { status } = await prisma.build.findUnique({ where: { id: buildId } }) if (status === 'canceled') { - throw new Error('Build canceled.') + throw new Error('Deployment canceled.') } if (isCache) { await saveBuildLog({ line: `Building cache image successful.`, buildId, applicationId }); diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index bb920b7ed..5d48c3f3b 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -93,7 +93,6 @@ export const asyncExecShellStream = async ({ debug, buildId, applicationId, comm const { execaCommand } = await import('execa') const subprocess = execaCommand(command, { env: { DOCKER_BUILDKIT: "1", DOCKER_HOST: engine } }) if (debug) { - await saveBuildLog({ line: `=========================`, buildId, applicationId }); subprocess.stdout.on('data', async (data) => { const stdout = data.toString(); const array = stdout.split('\n') @@ -123,7 +122,6 @@ export const asyncExecShellStream = async ({ debug, buildId, applicationId, comm } subprocess.on('exit', async (code) => { await asyncSleep(1000); - await saveBuildLog({ line: `=========================`, buildId, applicationId }); if (code === 0) { resolve(code) } else { @@ -1871,7 +1869,7 @@ export async function stopBuild(buildId, applicationId) { let count = 0; await new Promise(async (resolve, reject) => { const { destinationDockerId, status } = await prisma.build.findFirst({ where: { id: buildId } }); - const { engine, id: dockerId } = await prisma.destinationDocker.findFirst({ where: { id: destinationDockerId } }); + const { id: dockerId } = await prisma.destinationDocker.findFirst({ where: { id: destinationDockerId } }); const interval = setInterval(async () => { try { if (status === 'failed' || status === 'canceled') { @@ -1881,10 +1879,10 @@ export async function stopBuild(buildId, applicationId) { if (count > 15) { clearInterval(interval); if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage("action:flushQueue") + scheduler.workers.get('deployApplication').postMessage('cancel') } - await cleanupDB(buildId); - return reject(new Error('Build canceled')); + await cleanupDB(buildId, applicationId); + return reject(new Error('Deployment canceled.')); } const { stdout: buildContainers } = await executeDockerCmd({ dockerId, command: `docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'` }) if (buildContainers) { @@ -1896,9 +1894,9 @@ export async function stopBuild(buildId, applicationId) { await removeContainer({ id, dockerId }); clearInterval(interval); if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage("action:flushQueue") + scheduler.workers.get('deployApplication').postMessage('cancel') } - await cleanupDB(buildId); + await cleanupDB(buildId, applicationId); return resolve(); } } @@ -1909,11 +1907,12 @@ export async function stopBuild(buildId, applicationId) { }); } -async function cleanupDB(buildId: string) { +async function cleanupDB(buildId: string, applicationId: string) { const data = await prisma.build.findUnique({ where: { id: buildId } }); if (data?.status === 'queued' || data?.status === 'running') { await prisma.build.update({ where: { id: buildId }, data: { status: 'canceled' } }); } + await saveBuildLog({ line: 'Deployment canceled.', buildId, applicationId }); } export function convertTolOldVolumeNames(type) { diff --git a/apps/api/src/lib/scheduler.ts b/apps/api/src/lib/scheduler.ts index ad2dde568..6c233d050 100644 --- a/apps/api/src/lib/scheduler.ts +++ b/apps/api/src/lib/scheduler.ts @@ -9,27 +9,17 @@ Bree.extend(TSBree); const options: any = { defaultExtension: 'js', - logger: false, + logger: new Cabin(), workerMessageHandler: async ({ name, message }) => { - if (name === 'deployApplication') { - if (message.pending === 0 && message.size === 0) { - if (message.caller === 'autoUpdater') { - if (!scheduler.workers.has('autoUpdater')) { - await scheduler.run('autoUpdater') - } - } - if (message.caller === 'cleanupStorage') { - if (!scheduler.workers.has('cleanupStorage')) { - await scheduler.run('cleanupStorage') - } - } - + if (name === 'deployApplication' && message?.deploying) { + if (scheduler.workers.has('autoUpdater') || scheduler.workers.has('cleanupStorage')) { + scheduler.workers.get('deployApplication').postMessage('cancel') } } }, jobs: [ { - name: 'deployApplication' + name: 'deployApplication', }, { name: 'cleanupStorage', diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index 2abf30b3a..26b0d2bb9 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -75,7 +75,6 @@ export async function getApplicationStatus(request: FastifyRequest) { isExited = await isContainerExited(application.destinationDocker.id, id); } return { - isQueueActive: scheduler.workers.has('deployApplication'), isRunning, isExited, }; @@ -453,6 +452,8 @@ export async function deployApplication(request: FastifyRequest): Promi type: 'webhook_commit' } }); - scheduler.workers.get('deployApplication').postMessage({ - build_id: buildId, - type: 'webhook_commit', - ...applicationFound - }); - return { message: 'Queued. Thank you!' }; @@ -183,6 +177,8 @@ export async function gitHubEvents(request: FastifyRequest): Promi await prisma.build.create({ data: { id: buildId, + pullmergeRequestId, + sourceBranch, applicationId: applicationFound.id, destinationDockerId: applicationFound.destinationDocker.id, gitSourceId: applicationFound.gitSource.id, @@ -192,14 +188,7 @@ export async function gitHubEvents(request: FastifyRequest): Promi type: 'webhook_pr' } }); - scheduler.workers.get('deployApplication').postMessage({ - build_id: buildId, - type: 'webhook_pr', - ...applicationFound, - sourceBranch, - pullmergeRequestId - }); - + return { message: 'Queued. Thank you!' }; diff --git a/apps/api/src/routes/webhooks/gitlab/handlers.ts b/apps/api/src/routes/webhooks/gitlab/handlers.ts index 0e7f8ec5d..df848e45c 100644 --- a/apps/api/src/routes/webhooks/gitlab/handlers.ts +++ b/apps/api/src/routes/webhooks/gitlab/handlers.ts @@ -89,12 +89,6 @@ export async function gitLabEvents(request: FastifyRequest) { } }); - scheduler.workers.get('deployApplication').postMessage({ - build_id: buildId, - type: 'webhook_commit', - ...applicationFound - }); - return { message: 'Queued. Thank you!' }; @@ -141,6 +135,8 @@ export async function gitLabEvents(request: FastifyRequest) { await prisma.build.create({ data: { id: buildId, + pullmergeRequestId, + sourceBranch, applicationId: applicationFound.id, destinationDockerId: applicationFound.destinationDocker.id, gitSourceId: applicationFound.gitSource.id, @@ -150,14 +146,6 @@ export async function gitLabEvents(request: FastifyRequest) { type: 'webhook_mr' } }); - scheduler.workers.get('deployApplication').postMessage({ - build_id: buildId, - type: 'webhook_mr', - ...applicationFound, - sourceBranch, - pullmergeRequestId - }); - return { message: 'Queued. Thank you!' }; diff --git a/apps/ui/package.json b/apps/ui/package.json index 0a64124f0..ddff33ffd 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -14,20 +14,20 @@ "format": "prettier --write --plugin-search-dir=. ." }, "devDependencies": { - "@playwright/test": "1.24.2", + "@playwright/test": "1.25.1", "@sveltejs/kit": "1.0.0-next.405", "@types/js-cookie": "3.0.2", - "@typescript-eslint/eslint-plugin": "5.33.0", - "@typescript-eslint/parser": "5.33.0", + "@typescript-eslint/eslint-plugin": "5.35.1", + "@typescript-eslint/parser": "5.35.1", "autoprefixer": "10.4.8", - "eslint": "8.21.0", + "eslint": "8.22.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-svelte3": "4.0.0", "postcss": "8.4.16", "prettier": "2.7.1", "prettier-plugin-svelte": "2.7.0", "svelte": "3.49.0", - "svelte-check": "2.8.0", + "svelte-check": "2.8.1", "svelte-preprocess": "4.10.7", "tailwindcss": "3.1.8", "tailwindcss-scrollbar": "0.1.0", @@ -39,7 +39,7 @@ "dependencies": { "@sveltejs/adapter-static": "1.0.0-next.39", "cuid": "2.1.8", - "daisyui": "2.22.0", + "daisyui": "2.24.0", "js-cookie": "3.0.1", "p-limit": "4.0.0", "svelte-select": "4.4.7", diff --git a/apps/ui/src/routes/applications/[id]/__layout.svelte b/apps/ui/src/routes/applications/[id]/__layout.svelte index c40f66439..83f66dc6f 100644 --- a/apps/ui/src/routes/applications/[id]/__layout.svelte +++ b/apps/ui/src/routes/applications/[id]/__layout.svelte @@ -66,7 +66,6 @@ let loading = false; let statusInterval: any; - let isQueueActive = false; $disabledButton = !$appSession.isAdmin || (!application.fqdn && !application.settings.isBot) || @@ -121,7 +120,6 @@ if ($status.application.loading) return; $status.application.loading = true; const data = await get(`/applications/${id}/status`); - isQueueActive = data.isQueueActive; $status.application.isRunning = data.isRunning; $status.application.isExited = data.isExited; $status.application.loading = false; @@ -259,13 +257,10 @@
handleDeploySubmit(true)}>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45e590081..9fd8a9e23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,15 +23,15 @@ importers: '@fastify/static': 6.5.0 '@iarna/toml': 2.2.5 '@prisma/client': 3.15.2 - '@types/node': 18.7.11 + '@types/node': 18.7.13 '@types/node-os-utils': 1.3.0 - '@typescript-eslint/eslint-plugin': 5.34.0 - '@typescript-eslint/parser': 5.34.0 + '@typescript-eslint/eslint-plugin': 5.35.1 + '@typescript-eslint/parser': 5.35.1 axios: 0.27.2 bcryptjs: 2.4.3 bree: 9.1.2 cabin: 9.1.2 - compare-versions: 4.1.3 + compare-versions: 4.1.4 cuid: 2.1.8 dayjs: 1.11.5 dockerode: 3.3.4 @@ -52,7 +52,7 @@ importers: node-forge: 1.3.1 node-os-utils: 1.3.7 nodemon: 2.0.19 - p-queue: 7.3.0 + p-all: 4.0.0 prettier: 2.7.1 prisma: 3.15.2 public-ip: 6.0.1 @@ -63,7 +63,7 @@ importers: typescript: 4.7.4 unique-names-generator: 4.7.1 dependencies: - '@breejs/ts-worker': 2.0.0_vko2uubnomvm6mw63pxqdtopyq + '@breejs/ts-worker': 2.0.0_rzqxabipis2a5sxrpk4obdh4zu '@fastify/autoload': 5.2.0 '@fastify/cookie': 8.0.0 '@fastify/cors': 8.1.0 @@ -76,7 +76,7 @@ importers: bcryptjs: 2.4.3 bree: 9.1.2 cabin: 9.1.2 - compare-versions: 4.1.3 + compare-versions: 4.1.4 cuid: 2.1.8 dayjs: 1.11.5 dockerode: 3.3.4 @@ -92,16 +92,16 @@ importers: jsonwebtoken: 8.5.1 node-forge: 1.3.1 node-os-utils: 1.3.7 - p-queue: 7.3.0 + p-all: 4.0.0 public-ip: 6.0.1 ssh-config: 4.1.6 strip-ansi: 7.0.1 unique-names-generator: 4.7.1 devDependencies: - '@types/node': 18.7.11 + '@types/node': 18.7.13 '@types/node-os-utils': 1.3.0 - '@typescript-eslint/eslint-plugin': 5.34.0_euudt5oqhhodkyae5tf6wjmsda - '@typescript-eslint/parser': 5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/eslint-plugin': 5.35.1_ktjxjibzrfqejavile4bhmzhjq + '@typescript-eslint/parser': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq esbuild: 0.15.5 eslint: 8.22.0 eslint-config-prettier: 8.5.0_eslint@8.22.0 @@ -115,16 +115,16 @@ importers: apps/ui: specifiers: - '@playwright/test': 1.24.2 + '@playwright/test': 1.25.1 '@sveltejs/adapter-static': 1.0.0-next.39 '@sveltejs/kit': 1.0.0-next.405 '@types/js-cookie': 3.0.2 - '@typescript-eslint/eslint-plugin': 5.33.0 - '@typescript-eslint/parser': 5.33.0 + '@typescript-eslint/eslint-plugin': 5.35.1 + '@typescript-eslint/parser': 5.35.1 autoprefixer: 10.4.8 cuid: 2.1.8 - daisyui: 2.22.0 - eslint: 8.21.0 + daisyui: 2.24.0 + eslint: 8.22.0 eslint-config-prettier: 8.5.0 eslint-plugin-svelte3: 4.0.0 js-cookie: 3.0.1 @@ -133,7 +133,7 @@ importers: prettier: 2.7.1 prettier-plugin-svelte: 2.7.0 svelte: 3.49.0 - svelte-check: 2.8.0 + svelte-check: 2.8.1 svelte-preprocess: 4.10.7 svelte-select: 4.4.7 sveltekit-i18n: 2.2.2 @@ -145,26 +145,26 @@ importers: dependencies: '@sveltejs/adapter-static': 1.0.0-next.39 cuid: 2.1.8 - daisyui: 2.22.0_25hquoklqeoqwmt7fwvvcyxm5e + daisyui: 2.24.0_25hquoklqeoqwmt7fwvvcyxm5e js-cookie: 3.0.1 p-limit: 4.0.0 svelte-select: 4.4.7 sveltekit-i18n: 2.2.2_svelte@3.49.0 devDependencies: - '@playwright/test': 1.24.2 + '@playwright/test': 1.25.1 '@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.5 '@types/js-cookie': 3.0.2 - '@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/eslint-plugin': 5.35.1_ktjxjibzrfqejavile4bhmzhjq + '@typescript-eslint/parser': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq autoprefixer: 10.4.8_postcss@8.4.16 - eslint: 8.21.0 - eslint-config-prettier: 8.5.0_eslint@8.21.0 - eslint-plugin-svelte3: 4.0.0_a7wk4ghvg4hia4trwaglu7p6cq + eslint: 8.22.0 + eslint-config-prettier: 8.5.0_eslint@8.22.0 + eslint-plugin-svelte3: 4.0.0_laaqauvsmoyypsiqkozwyi2fn4 postcss: 8.4.16 prettier: 2.7.1 prettier-plugin-svelte: 2.7.0_o3ioganyptcsrh6x4hnxvjkpqi svelte: 3.49.0 - svelte-check: 2.8.0_vylzxgme5yisu3bsyvcau4hjtq + svelte-check: 2.8.1_vylzxgme5yisu3bsyvcau4hjtq svelte-preprocess: 4.10.7_fje22ktja5v2dh6nbkissncqme tailwindcss: 3.1.8 tailwindcss-scrollbar: 0.1.0_tailwindcss@3.1.8 @@ -211,14 +211,14 @@ packages: engines: {node: '>= 10'} dev: false - /@breejs/ts-worker/2.0.0_vko2uubnomvm6mw63pxqdtopyq: + /@breejs/ts-worker/2.0.0_rzqxabipis2a5sxrpk4obdh4zu: resolution: {integrity: sha512-6anHRcmgYlF7mrm/YVRn6rx2cegLuiY3VBxkkimOTWC/dVQeH336imVSuIKEGKTwiuNTPr2hswVdDSneNuXg3A==} engines: {node: '>= 12.11'} peerDependencies: bree: '>=9.0.0' dependencies: bree: 9.1.2 - ts-node: 10.8.2_yuzmdm4dwbtqx7oi4ad7vmtwja + ts-node: 10.8.2_57uwcby55h6tzvkj3v5sfcgxoe tsconfig-paths: 4.1.0 transitivePeerDependencies: - '@swc/core' @@ -401,13 +401,13 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 - /@playwright/test/1.24.2: - resolution: {integrity: sha512-Q4X224pRHw4Dtkk5PoNJplZCokLNvVbXD9wDQEMrHcEuvWpJWEQDeJ9gEwkZ3iCWSFSWBshIX177B231XW4wOQ==} + /@playwright/test/1.25.1: + resolution: {integrity: sha512-IJ4X0yOakXtwkhbnNzKkaIgXe6df7u3H3FnuhI9Jqh+CdO0e/lYQlDLYiyI9cnXK8E7UAppAWP+VqAv6VX7HQg==} engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.6.5 - playwright-core: 1.24.2 + '@types/node': 18.7.13 + playwright-core: 1.25.1 dev: true /@prisma/client/3.15.2_prisma@3.15.2: @@ -534,7 +534,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.7.11 + '@types/node': 18.7.13 '@types/responselike': 1.0.0 dev: false @@ -557,7 +557,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.7.11 + '@types/node': 18.7.13 dev: false /@types/lodash/4.14.182: @@ -568,16 +568,8 @@ packages: resolution: {integrity: sha512-XwVteWQx/XkfRPyaGkw8dEbrCAkoRZ73pI3XznUYIpzbCfpQB3UnDlR5TnmdhetlT889tUJGF8QWo9xrgTpsiA==} dev: true - /@types/node/18.0.3: - resolution: {integrity: sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==} - dev: true - - /@types/node/18.6.5: - resolution: {integrity: sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==} - dev: true - - /@types/node/18.7.11: - resolution: {integrity: sha512-KZhFpSLlmK/sdocfSAjqPETTMd0ug6HIMIAwkwUpU79olnZdQtMxpQP+G1wDzCH7na+FltSIhbaZuKdwZ8RDrw==} + /@types/node/18.7.13: + resolution: {integrity: sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==} /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -590,17 +582,17 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.7.11 + '@types/node': 18.7.13 dev: false /@types/sass/1.43.1: resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} dependencies: - '@types/node': 18.0.3 + '@types/node': 18.7.13 dev: true - /@typescript-eslint/eslint-plugin/5.33.0_njno5y7ry2l2lcmiu4tywxkwnq: - resolution: {integrity: sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==} + /@typescript-eslint/eslint-plugin/5.35.1_ktjxjibzrfqejavile4bhmzhjq: + resolution: {integrity: sha512-RBZZXZlI4XCY4Wzgy64vB+0slT9+yAPQRjj/HSaRwUot33xbDjF1oN9BLwOLTewoOI0jothIltZRe9uJCHf8gg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -610,37 +602,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/type-utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - debug: 4.3.4 - eslint: 8.21.0 - functional-red-black-tree: 1.0.1 - ignore: 5.2.0 - regexpp: 3.2.0 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/eslint-plugin/5.34.0_euudt5oqhhodkyae5tf6wjmsda: - resolution: {integrity: sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/parser': 5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq - '@typescript-eslint/scope-manager': 5.34.0 - '@typescript-eslint/type-utils': 5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq - '@typescript-eslint/utils': 5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/parser': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/scope-manager': 5.35.1 + '@typescript-eslint/type-utils': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/utils': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq debug: 4.3.4 eslint: 8.22.0 functional-red-black-tree: 1.0.1 @@ -653,8 +618,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==} + /@typescript-eslint/parser/5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-XL2TBTSrh3yWAsMYpKseBYTVpvudNf69rPOWXWVBI08My2JVT5jR66eTt4IgQFHA/giiKJW5dUD4x/ZviCKyGg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -663,29 +628,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 - debug: 4.3.4 - eslint: 8.21.0 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser/5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq: - resolution: {integrity: sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.34.0 - '@typescript-eslint/types': 5.34.0 - '@typescript-eslint/typescript-estree': 5.34.0_typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.35.1 + '@typescript-eslint/types': 5.35.1 + '@typescript-eslint/typescript-estree': 5.35.1_typescript@4.7.4 debug: 4.3.4 eslint: 8.22.0 typescript: 4.7.4 @@ -693,24 +638,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager/5.33.0: - resolution: {integrity: sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw==} + /@typescript-eslint/scope-manager/5.35.1: + resolution: {integrity: sha512-kCYRSAzIW9ByEIzmzGHE50NGAvAP3wFTaZevgWva7GpquDyFPFcmvVkFJGWJJktg/hLwmys/FZwqM9EKr2u24Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/visitor-keys': 5.33.0 + '@typescript-eslint/types': 5.35.1 + '@typescript-eslint/visitor-keys': 5.35.1 dev: true - /@typescript-eslint/scope-manager/5.34.0: - resolution: {integrity: sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.34.0 - '@typescript-eslint/visitor-keys': 5.34.0 - dev: true - - /@typescript-eslint/type-utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==} + /@typescript-eslint/type-utils/5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-8xT8ljvo43Mp7BiTn1vxLXkjpw8wS4oAc00hMSB4L1/jIiYbjjnc3Qp2GAUOG/v8zsNCd1qwcqfCQ0BuishHkw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -719,26 +656,7 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - debug: 4.3.4 - eslint: 8.21.0 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/type-utils/5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq: - resolution: {integrity: sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/utils': 5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/utils': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq debug: 4.3.4 eslint: 8.22.0 tsutils: 3.21.0_typescript@4.7.4 @@ -747,18 +665,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types/5.33.0: - resolution: {integrity: sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw==} + /@typescript-eslint/types/5.35.1: + resolution: {integrity: sha512-FDaujtsH07VHzG0gQ6NDkVVhi1+rhq0qEvzHdJAQjysN+LHDCKDKCBRlZFFE0ec0jKxiv0hN63SNfExy0KrbQQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types/5.34.0: - resolution: {integrity: sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@typescript-eslint/typescript-estree/5.33.0_typescript@4.7.4: - resolution: {integrity: sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==} + /@typescript-eslint/typescript-estree/5.35.1_typescript@4.7.4: + resolution: {integrity: sha512-JUqE1+VRTGyoXlDWWjm6MdfpBYVq+hixytrv1oyjYIBEOZhBCwtpp5ZSvBt4wIA1MKWlnaC2UXl2XmYGC3BoQA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -766,8 +679,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/visitor-keys': 5.33.0 + '@typescript-eslint/types': 5.35.1 + '@typescript-eslint/visitor-keys': 5.35.1 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -778,55 +691,16 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.34.0_typescript@4.7.4: - resolution: {integrity: sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.34.0 - '@typescript-eslint/visitor-keys': 5.34.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==} + /@typescript-eslint/utils/5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-v6F8JNXgeBWI4pzZn36hT2HXXzoBBBJuOYvoQiaQaEEjdi5STzux3Yj8v7ODIpx36i/5s8TdzuQ54TPc5AITQQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 - eslint: 8.21.0 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.21.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/utils/5.34.0_4rv7y5c6xz3vfxwhbrcxxi73bq: - resolution: {integrity: sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.34.0 - '@typescript-eslint/types': 5.34.0 - '@typescript-eslint/typescript-estree': 5.34.0_typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.35.1 + '@typescript-eslint/types': 5.35.1 + '@typescript-eslint/typescript-estree': 5.35.1_typescript@4.7.4 eslint: 8.22.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@8.22.0 @@ -835,19 +709,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys/5.33.0: - resolution: {integrity: sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw==} + /@typescript-eslint/visitor-keys/5.35.1: + resolution: {integrity: sha512-cEB1DvBVo1bxbW/S5axbGPE6b7FIMAbo3w+AGq6zNDA7+NYJOIkKj/sInfTv4edxd4PxJSgdN4t6/pbvgA+n5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.33.0 - eslint-visitor-keys: 3.3.0 - dev: true - - /@typescript-eslint/visitor-keys/5.34.0: - resolution: {integrity: sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.34.0 + '@typescript-eslint/types': 5.35.1 eslint-visitor-keys: 3.3.0 dev: true @@ -2073,8 +1939,8 @@ packages: delayed-stream: 1.0.0 dev: false - /compare-versions/4.1.3: - resolution: {integrity: sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==} + /compare-versions/4.1.4: + resolution: {integrity: sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==} dev: false /component-emitter/1.3.0: @@ -2224,8 +2090,8 @@ packages: resolution: {integrity: sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==} dev: false - /daisyui/2.22.0_25hquoklqeoqwmt7fwvvcyxm5e: - resolution: {integrity: sha512-H08aQP+Mfl2fQOmyaxd1NP54gQbr2xRUj2MmFFvfJ5EDGbthVBICDMNFKyia/zBfR58G8eTJo3CmydglM81/7Q==} + /daisyui/2.24.0_25hquoklqeoqwmt7fwvvcyxm5e: + resolution: {integrity: sha512-Fdu/4LCdTfWLWAbCuPxvnaRotEfJ+hVPgZ2kv/aUk9RZ00Yk8fGdJtIf0kXJ3IgUKOr8rCXUpfQY6DQU9usPCQ==} peerDependencies: autoprefixer: ^10.0.2 postcss: ^8.1.6 @@ -2984,15 +2850,6 @@ packages: engines: {node: '>=12'} dev: false - /eslint-config-prettier/8.5.0_eslint@8.21.0: - resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 8.21.0 - dev: true - /eslint-config-prettier/8.5.0_eslint@8.22.0: resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} hasBin: true @@ -3019,13 +2876,13 @@ packages: prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-svelte3/4.0.0_a7wk4ghvg4hia4trwaglu7p6cq: + /eslint-plugin-svelte3/4.0.0_laaqauvsmoyypsiqkozwyi2fn4: resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==} peerDependencies: eslint: '>=8.0.0' svelte: ^3.2.0 dependencies: - eslint: 8.21.0 + eslint: 8.22.0 svelte: 3.49.0 dev: true @@ -3045,16 +2902,6 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.21.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.21.0 - eslint-visitor-keys: 2.1.0 - dev: true - /eslint-utils/3.0.0_eslint@8.22.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} @@ -3075,54 +2922,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.21.0: - resolution: {integrity: sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint/eslintrc': 1.3.0 - '@humanwhocodes/config-array': 0.10.4 - '@humanwhocodes/gitignore-to-minimatch': 1.0.2 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.21.0 - eslint-visitor-keys: 3.3.0 - espree: 9.3.3 - esquery: 1.4.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - functional-red-black-tree: 1.0.1 - glob-parent: 6.0.2 - globals: 13.15.0 - globby: 11.1.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.0 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - v8-compile-cache: 2.3.0 - transitivePeerDependencies: - - supports-color - dev: true - /eslint/8.22.0: resolution: {integrity: sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3223,10 +3022,6 @@ packages: engines: {node: '>=6'} dev: false - /eventemitter3/4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - dev: false - /execa/6.1.0: resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4703,6 +4498,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /p-all/4.0.0: + resolution: {integrity: sha512-QXqMc8PpYu0gmNM6VcKP0uYqeI+dtvSNeaDb8ktnNjposr+nftHHCSYbj/S/oUceF6R868jw1XOxkJKUSiHgEQ==} + engines: {node: '>=12.20'} + dependencies: + p-map: 5.5.0 + dev: false + /p-cancelable/3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -4754,12 +4556,11 @@ packages: p-limit: 3.1.0 dev: true - /p-queue/7.3.0: - resolution: {integrity: sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==} + /p-map/5.5.0: + resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} engines: {node: '>=12'} dependencies: - eventemitter3: 4.0.7 - p-timeout: 5.0.2 + aggregate-error: 4.0.1 dev: false /p-timeout/3.2.0: @@ -4769,11 +4570,6 @@ packages: p-finally: 1.0.0 dev: false - /p-timeout/5.0.2: - resolution: {integrity: sha512-sEmji9Yaq+Tw+STwsGAE56hf7gMy9p0tQfJojIAamB7WHJYJKf1qlsg9jqBWG8q9VCxKPhZaP/AcXwEoBcYQhQ==} - engines: {node: '>=12'} - dev: false - /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -4954,8 +4750,8 @@ packages: find-up: 3.0.0 dev: false - /playwright-core/1.24.2: - resolution: {integrity: sha512-zfAoDoPY/0sDLsgSgLZwWmSCevIg1ym7CppBwllguVBNiHeixZkc1AdMuYUPZC6AdEYc4CxWEyLMBTw2YcmRrA==} + /playwright-core/1.25.1: + resolution: {integrity: sha512-lSvPCmA2n7LawD2Hw7gSCLScZ+vYRkhU8xH0AapMyzwN+ojoDqhkH/KIEUxwNu2PjPoE/fcE0wLAksdOhJ2O5g==} engines: {node: '>=14'} hasBin: true dev: true @@ -5751,8 +5547,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /svelte-check/2.8.0_vylzxgme5yisu3bsyvcau4hjtq: - resolution: {integrity: sha512-HRL66BxffMAZusqe5I5k26mRWQ+BobGd9Rxm3onh7ZVu0nTk8YTKJ9vu3LVPjUGLU9IX7zS+jmwPVhJYdXJ8vg==} + /svelte-check/2.8.1_vylzxgme5yisu3bsyvcau4hjtq: + resolution: {integrity: sha512-cibyY1sgt3ONIDnQbSgV2X9AJFhwEslRHNo95lijrYfPzVEvTvbmL2ohsUyqB5L7j1GhLXtQbjCJ4lZZ/fwbeQ==} hasBin: true peerDependencies: svelte: ^3.24.0 @@ -5988,7 +5784,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ts-node/10.8.2_yuzmdm4dwbtqx7oi4ad7vmtwja: + /ts-node/10.8.2_57uwcby55h6tzvkj3v5sfcgxoe: resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} hasBin: true peerDependencies: @@ -6007,7 +5803,7 @@ packages: '@tsconfig/node12': 1.0.9 '@tsconfig/node14': 1.0.1 '@tsconfig/node16': 1.0.2 - '@types/node': 18.7.11 + '@types/node': 18.7.13 acorn: 8.8.0 acorn-walk: 8.2.0 arg: 4.1.3