Merge pull request #565 from coollabsio/next

v3.8.2
This commit is contained in:
Andras Bacsai 2022-08-26 15:22:27 +02:00 committed by GitHub
commit 5360c60f3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1162 additions and 1369 deletions

View File

@ -22,12 +22,13 @@
"@fastify/jwt": "6.3.2",
"@fastify/static": "6.5.0",
"@iarna/toml": "2.2.5",
"@ladjs/graceful": "3.0.2",
"@prisma/client": "3.15.2",
"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",
@ -43,17 +44,18 @@
"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",
"p-throttle": "5.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",

View File

@ -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;

View File

@ -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;

View File

@ -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())

View File

@ -5,11 +5,10 @@ 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';
import Graceful from '@ladjs/graceful'
declare module 'fastify' {
interface FastifyInstance {
config: {
@ -104,49 +103,39 @@ 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
// Check for update
const graceful = new Graceful({ brees: [scheduler] });
graceful.listen();
setInterval(async () => {
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
if (isAutoUpdateEnabled) {
const currentVersion = version;
const { data: versions } = await axios
.get(
`https://get.coollabs.io/versions.json`
, {
params: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
})
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')) {
scheduler.run('deployApplication');
}
if (!scheduler.workers.has('infrastructure')) {
scheduler.run('infrastructure');
}
}, 2000)
// autoUpdater
setInterval(async () => {
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:autoUpdater")
}, isDev ? 5000 : 60000 * 15)
// Cleanup storage
// cleanupStorage
setInterval(async () => {
if (scheduler.workers.has('deployApplication')) {
scheduler.workers.get('deployApplication').postMessage("status:cleanupStorage");
}
}, isDev ? 5000 : 60000 * 10)
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
}, isDev ? 6000 : 60000 * 10)
// checkProxies
setInterval(async () => {
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkProxies")
}, 10000)
// cleanupPrismaEngines
// setInterval(async () => {
// scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupPrismaEngines")
// }, 60000)
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 +159,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 {

View File

@ -1,43 +0,0 @@
import axios from 'axios';
import compareVersions from 'compare-versions';
import { parentPort } from 'node:worker_threads';
import { asyncExecShell, asyncSleep, isDev, prisma, version } from '../lib/common';
(async () => {
if (parentPort) {
try {
const currentVersion = version;
const { data: versions } = await axios
.get(
`https://get.coollabs.io/versions.json`
, {
params: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
})
const latestVersion = versions['coolify'].main.version;
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
if (isUpdateAvailable === 1) {
const activeCount = 0
if (activeCount === 0) {
if (!isDev) {
console.log(`Updating Coolify to ${latestVersion}.`);
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
await asyncExecShell(`env | grep COOLIFY > .env`);
await asyncExecShell(
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"`
);
} else {
console.log('Updating (not really in dev mode).');
}
}
}
} catch (error) {
console.log(error);
} finally {
await prisma.$disconnect();
}
} else process.exit(0);
})();

View File

@ -1,96 +0,0 @@
import { parentPort } from 'node:worker_threads';
import { prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, executeDockerCmd, listSettings } from '../lib/common';
import { checkContainer } from '../lib/docker';
(async () => {
if (parentPort) {
try {
const { arch } = await listSettings();
// Coolify Proxy local
const engine = '/var/run/docker.sock';
const localDocker = await prisma.destinationDocker.findFirst({
where: { engine, network: 'coolify' }
});
if (localDocker && localDocker.isCoolifyProxyUsed) {
// Remove HAProxy
const found = await checkContainer({ dockerId: localDocker.id, container: 'coolify-haproxy' });
if (found) {
await executeDockerCmd({
dockerId: localDocker.id,
command: `docker stop -t 0 coolify-haproxy && docker rm coolify-haproxy`
})
}
await startTraefikProxy(localDocker.id);
}
// TCP Proxies
const databasesWithPublicPort = await prisma.database.findMany({
where: { publicPort: { not: null } },
include: { settings: true, destinationDocker: true }
});
for (const database of databasesWithPublicPort) {
const { destinationDockerId, destinationDocker, publicPort, id } = database;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
const { privatePort } = generateDatabaseConfiguration(database, arch);
// Remove HAProxy
const found = await checkContainer({
dockerId: localDocker.id, container: `haproxy-for-${publicPort}`
});
if (found) {
await executeDockerCmd({
dockerId: localDocker.id,
command: `docker stop -t 0 haproxy-for-${publicPort} && docker rm haproxy-for-${publicPort}`
})
}
await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
}
}
const wordpressWithFtp = await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null } },
include: { service: { include: { destinationDocker: true } } }
});
for (const ftp of wordpressWithFtp) {
const { service, ftpPublicPort } = ftp;
const { destinationDockerId, destinationDocker, id } = service;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
// Remove HAProxy
const found = await checkContainer({ dockerId: localDocker.id, container: `haproxy-for-${ftpPublicPort}` });
if (found) {
await executeDockerCmd({
dockerId: localDocker.id,
command: `docker stop -t 0 haproxy -for-${ftpPublicPort} && docker rm haproxy-for-${ftpPublicPort}`
})
}
await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp');
}
}
// HTTP Proxies
const minioInstances = await prisma.minio.findMany({
where: { publicPort: { not: null } },
include: { service: { include: { destinationDocker: true } } }
});
for (const minio of minioInstances) {
const { service, publicPort } = minio;
const { destinationDockerId, destinationDocker, id } = service;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
// Remove HAProxy
const found = await checkContainer({ dockerId: localDocker.id, container: `${id}-${publicPort}` });
if (found) {
await executeDockerCmd({
dockerId: localDocker.id,
command: `docker stop -t 0 ${id}-${publicPort} && docker rm ${id}-${publicPort} `
})
}
await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
}
}
} catch (error) {
} finally {
await prisma.$disconnect();
}
} else process.exit(0);
})();

View File

@ -1,19 +0,0 @@
import { parentPort } from 'node:worker_threads';
import { asyncExecShell, isDev, prisma } from '../lib/common';
(async () => {
if (parentPort) {
if (!isDev) {
try {
const { stdout } = await asyncExecShell(`ps -ef | grep /app/prisma-engines/query-engine | grep -v grep | wc -l | xargs`)
if (stdout.trim() != null && stdout.trim() != '' && Number(stdout.trim()) > 1) {
await asyncExecShell(`killall -q -e /app/prisma-engines/query-engine -o 10m`)
}
} catch (error) {
console.log(error);
} finally {
await prisma.$disconnect();
}
}
} else process.exit(0);
})();

View File

@ -1,60 +0,0 @@
import { parentPort } from 'node:worker_threads';
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, version } from '../lib/common';
(async () => {
if (parentPort) {
const destinationDockers = await prisma.destinationDocker.findMany();
let enginesDone = new Set()
for (const destination of destinationDockers) {
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
if (!isDev) {
const output = await executeDockerCmd({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'` })
stdout = output.stdout;
} else {
const output = await asyncExecShell(
`df -kPT /`
);
stdout = output.stdout;
}
let lines = stdout.trim().split('\n');
let header = lines[0];
let regex =
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
const boundaries = [];
let match;
while ((match = regex.exec(header))) {
boundaries.push(match[0].length);
}
boundaries[boundaries.length - 1] = -1;
const data = lines.slice(1).map((line) => {
const cl = boundaries.map((boundary) => {
const column = boundary > 0 ? line.slice(0, boundary) : line;
line = line.slice(boundary);
return column.trim();
});
return {
capacity: Number.parseInt(cl[5], 10) / 100
};
});
if (data.length > 0) {
const { capacity } = data[0];
if (capacity > 0.8) {
lowDiskSpace = true;
}
}
} catch (error) {
console.log(error);
}
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
}
await prisma.$disconnect();
} else process.exit(0);
})();

View File

@ -10,208 +10,269 @@ 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;
}
if (message === 'error') throw new Error('oops');
if (message === 'cancel') {
parentPort.postMessage('cancelled');
await prisma.$disconnect()
process.exit(0);
}
});
const pThrottle = await import('p-throttle')
const throttle = pThrottle.default({
limit: 1,
interval: 2000
});
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({
const th = throttle(async () => {
try {
const queuedBuilds = await prisma.build.findMany({ where: { status: 'queued' }, orderBy: { createdAt: 'asc' } });
const { concurrentBuilds } = await prisma.setting.findFirst({})
if (queuedBuilds.length > 0) {
parentPort.postMessage({ deploying: true });
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,
deploymentType,
denoOptions,
exposePort,
baseImage,
baseBuildImage,
deploymentType,
} = application
let {
branch,
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}`;
}
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 {
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;
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}`;
}
} 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);
let deployNeeded = true;
let destinationType;
if (forceRebuild) deployNeeded = true
if (!imageFound || deployNeeded) {
// if (true) {
if (buildpacks[buildPack])
await buildpacks[buildPack]({
dockerId: destinationDocker.id,
buildId,
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,
domain,
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,
@ -219,148 +280,93 @@ import * as buildpacks from '../lib/buildPacks';
repository,
branch,
projectId,
publishDirectory,
debug,
commit,
tag,
workdir,
port: exposePort ? `${exposePort}:${port}` : port,
commit,
installCommand,
buildCommand,
startCommand,
baseDirectory,
secrets,
phpModules,
pythonWSGI,
pythonModule,
pythonVariable,
dockerFileLocation,
denoMainFile,
denoOptions,
baseImage,
baseBuildImage,
deploymentType
publishDirectory
});
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}`);
}
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 }
});
}
}
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 });
catch (error) {
await prisma.build.updateMany({
where: { id: message.build_id, status: { in: ['queued', 'running'] } },
where: { id: buildId, status: { in: ['queued', 'running'] } },
data: { status: 'failed' }
});
throw new Error(error);
await saveBuildLog({ line: error, buildId, applicationId });
}
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();
await pAll.default(actions, { concurrency })
}
} catch (error) {
} finally {
}
});
})
while (true) {
await th()
}
} else process.exit(0);
})();

View File

@ -0,0 +1,216 @@
import { parentPort } from 'node:worker_threads';
import axios from 'axios';
import compareVersions from 'compare-versions';
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version } from '../lib/common';
async function disconnect() {
await prisma.$disconnect();
}
async function autoUpdater() {
try {
const currentVersion = version;
const { data: versions } = await axios
.get(
`https://get.coollabs.io/versions.json`
, {
params: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
})
const latestVersion = versions['coolify'].main.version;
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
if (isUpdateAvailable === 1) {
const activeCount = 0
if (activeCount === 0) {
if (!isDev) {
console.log(`Updating Coolify to ${latestVersion}.`);
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
await asyncExecShell(`env | grep COOLIFY > .env`);
await asyncExecShell(
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"`
);
} else {
console.log('Updating (not really in dev mode).');
}
}
}
} catch (error) {
console.log(error);
}
}
async function checkProxies() {
try {
const { default: isReachable } = await import('is-port-reachable');
let portReachable;
const { arch, ipv4, ipv6 } = await listSettings();
// Coolify Proxy local
const engine = '/var/run/docker.sock';
const localDocker = await prisma.destinationDocker.findFirst({
where: { engine, network: 'coolify' }
});
if (localDocker && localDocker.isCoolifyProxyUsed) {
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
if (!portReachable) {
await startTraefikProxy(localDocker.id);
}
}
// TCP Proxies
const databasesWithPublicPort = await prisma.database.findMany({
where: { publicPort: { not: null } },
include: { settings: true, destinationDocker: true }
});
for (const database of databasesWithPublicPort) {
const { destinationDockerId, destinationDocker, publicPort, id } = database;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
const { privatePort } = generateDatabaseConfiguration(database, arch);
portReachable = await isReachable(publicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
if (!portReachable) {
await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
}
}
}
const wordpressWithFtp = await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null } },
include: { service: { include: { destinationDocker: true } } }
});
for (const ftp of wordpressWithFtp) {
const { service, ftpPublicPort } = ftp;
const { destinationDockerId, destinationDocker, id } = service;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
portReachable = await isReachable(ftpPublicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
if (!portReachable) {
await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp');
}
}
}
// HTTP Proxies
const minioInstances = await prisma.minio.findMany({
where: { publicPort: { not: null } },
include: { service: { include: { destinationDocker: true } } }
});
for (const minio of minioInstances) {
const { service, publicPort } = minio;
const { destinationDockerId, destinationDocker, id } = service;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
portReachable = await isReachable(publicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
if (!portReachable) {
await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
}
}
}
} catch (error) {
}
}
async function cleanupPrismaEngines() {
if (!isDev) {
try {
const { stdout } = await asyncExecShell(`ps -ef | grep /app/prisma-engines/query-engine | grep -v grep | wc -l | xargs`)
if (stdout.trim() != null && stdout.trim() != '' && Number(stdout.trim()) > 1) {
await asyncExecShell(`killall -q -e /app/prisma-engines/query-engine -o 1m`)
}
} catch (error) {
console.log(error);
}
}
}
async function cleanupStorage() {
const destinationDockers = await prisma.destinationDocker.findMany();
let enginesDone = new Set()
for (const destination of destinationDockers) {
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
if (!isDev) {
const output = await executeDockerCmd({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'` })
stdout = output.stdout;
} else {
const output = await asyncExecShell(
`df -kPT /`
);
stdout = output.stdout;
}
let lines = stdout.trim().split('\n');
let header = lines[0];
let regex =
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
const boundaries = [];
let match;
while ((match = regex.exec(header))) {
boundaries.push(match[0].length);
}
boundaries[boundaries.length - 1] = -1;
const data = lines.slice(1).map((line) => {
const cl = boundaries.map((boundary) => {
const column = boundary > 0 ? line.slice(0, boundary) : line;
line = line.slice(boundary);
return column.trim();
});
return {
capacity: Number.parseInt(cl[5], 10) / 100
};
});
if (data.length > 0) {
const { capacity } = data[0];
if (capacity > 0.8) {
lowDiskSpace = true;
}
}
} catch (error) {
console.log(error);
}
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
}
}
(async () => {
let status = {
cleanupStorage: false,
autoUpdater: false
}
if (parentPort) {
parentPort.on('message', async (message) => {
if (parentPort) {
if (message === 'error') throw new Error('oops');
if (message === 'cancel') {
parentPort.postMessage('cancelled');
process.exit(1);
}
if (message === 'action:cleanupStorage') {
if (!status.autoUpdater) {
status.cleanupStorage = true
await cleanupStorage();
status.cleanupStorage = false
}
return;
}
if (message === 'action:cleanupPrismaEngines') {
await cleanupPrismaEngines();
return;
}
if (message === 'action:checkProxies') {
await checkProxies();
return;
}
if (message === 'action:autoUpdater') {
if (!status.cleanupStorage) {
status.autoUpdater = true
await autoUpdater();
status.autoUpdater = false
}
return;
}
}
});
} else process.exit(0);
})();

View File

@ -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 });

View File

@ -19,7 +19,7 @@ import * as serviceFields from './serviceFields'
import { saveBuildLog } from './buildPacks/common';
import { scheduler } from './scheduler';
export const version = '3.8.1';
export const version = '3.8.2';
export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr';
@ -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 {
@ -136,9 +134,33 @@ export const asyncExecShellStream = async ({ debug, buildId, applicationId, comm
export const asyncSleep = (delay: number): Promise<unknown> =>
new Promise((resolve) => setTimeout(resolve, delay));
export const prisma = new PrismaClient({
errorFormat: 'minimal'
errorFormat: 'minimal',
// log: [
// {
// emit: 'event',
// level: 'query',
// },
// {
// emit: 'stdout',
// level: 'error',
// },
// {
// emit: 'stdout',
// level: 'info',
// },
// {
// emit: 'stdout',
// level: 'warn',
// },
// ],
});
// prisma.$on('query', (e) => {
// console.log({e})
// console.log('Query: ' + e.query)
// console.log('Params: ' + e.params)
// console.log('Duration: ' + e.duration + 'ms')
// })
export const base64Encode = (text: string): string => {
return Buffer.from(text).toString('base64');
};
@ -1871,7 +1893,7 @@ export async function stopBuild(buildId, applicationId) {
let count = 0;
await new Promise<void>(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 +1903,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 +1918,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 +1931,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) {

View File

@ -9,48 +9,22 @@ Bree.extend(TSBree);
const options: any = {
defaultExtension: 'js',
// logger: new Cabin(),
logger: false,
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.stop('deployApplication');
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: 'cleanupStorage',
},
{
name: 'cleanupPrismaEngines',
interval: '1m'
},
{
name: 'checkProxies',
interval: '10s'
},
{
name: 'autoUpdater',
}
{ name: 'infrastructure' },
{ name: 'deployApplication' },
],
};
if (isDev) options.root = path.join(__dirname, '../jobs');
export const scheduler = new Bree(options);

View File

@ -75,7 +75,6 @@ export async function getApplicationStatus(request: FastifyRequest<OnlyId>) {
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<DeployApplicatio
id: buildId,
applicationId: id,
branch: application.branch,
pullmergeRequestId,
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
gitSourceId: application.gitSource?.id,
githubAppId: application.gitSource?.githubApp?.id,
@ -461,24 +462,6 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
type: 'manual'
}
});
if (pullmergeRequestId) {
scheduler.workers.get('deployApplication').postMessage({
build_id: buildId,
type: 'manual',
...application,
sourceBranch: branch,
pullmergeRequestId,
forceRebuild
});
} else {
scheduler.workers.get('deployApplication').postMessage({
build_id: buildId,
type: 'manual',
...application,
forceRebuild
});
}
return {
buildId
};

View File

@ -73,6 +73,23 @@ export async function update(request: FastifyRequest<Update>) {
return errorHandler({ status, message })
}
}
export async function restartCoolify(request: FastifyRequest<any>) {
try {
const teamId = request.user.teamId;
if (teamId === '0') {
if (!isDev) {
await asyncExecShell(`docker restart coolify`);
return {};
} else {
console.log('Restarting Coolify')
return {};
}
}
throw { status: 500, message: 'You are not authorized to restart Coolify.' };
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
export async function showUsage() {
try {
return {
@ -101,7 +118,8 @@ export async function showDashboard(request: FastifyRequest) {
include: { settings: true }
});
const databases = await prisma.database.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true }
});
const services = await prisma.service.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }

View File

@ -1,5 +1,5 @@
import { FastifyPluginAsync } from 'fastify';
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser, cleanupManually } from './handlers';
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser, cleanupManually, restartCoolify } from './handlers';
import { GetCurrentUser } from './types';
export interface Update {
@ -47,6 +47,10 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
onRequest: [fastify.authenticate]
}, async () => await showUsage());
fastify.post('/internal/restart', {
onRequest: [fastify.authenticate]
}, async (request) => await restartCoolify(request));
fastify.post('/internal/cleanup', {
onRequest: [fastify.authenticate]
}, async () => await cleanupManually());

View File

@ -142,12 +142,6 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): 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<GitHubEvents>): 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<GitHubEvents>): Promi
type: 'webhook_pr'
}
});
scheduler.workers.get('deployApplication').postMessage({
build_id: buildId,
type: 'webhook_pr',
...applicationFound,
sourceBranch,
pullmergeRequestId
});
return {
message: 'Queued. Thank you!'
};

View File

@ -89,12 +89,6 @@ export async function gitLabEvents(request: FastifyRequest<GitLabEvents>) {
}
});
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<GitLabEvents>) {
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<GitLabEvents>) {
type: 'webhook_mr'
}
});
scheduler.workers.get('deployApplication').postMessage({
build_id: buildId,
type: 'webhook_mr',
...applicationFound,
sourceBranch,
pullmergeRequestId
});
return {
message: 'Queued. Thank you!'
};

View File

@ -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",
@ -38,8 +38,9 @@
"type": "module",
"dependencies": {
"@sveltejs/adapter-static": "1.0.0-next.39",
"@tailwindcss/typography": "^0.5.4",
"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",

View File

@ -20,11 +20,12 @@
let usageInterval: any;
let loading = {
usage: false,
cleanup: false
cleanup: false,
restart: false
};
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
import { onDestroy, onMount } from 'svelte';
import { get } from '$lib/api';
import { get, post } from '$lib/api';
import { errorNotification } from '$lib/common';
async function getStatus() {
if (loading.usage) return;
@ -33,6 +34,25 @@
usage = data.usage;
loading.usage = false;
}
async function restartCoolify() {
const sure = confirm(
'Are you sure you would like to restart Coolify? Currently running deployments will be stopped and restarted.'
);
if (sure) {
loading.restart = true;
try {
await post(`/internal/restart`, {});
addToast({
type: 'success',
message: 'Coolify restarted successfully. It will take a moment.'
});
} catch (error) {
return errorNotification(error);
} finally {
loading.restart = false;
}
}
}
onDestroy(() => {
clearInterval(usageInterval);
});
@ -48,65 +68,106 @@
return errorNotification(error);
}
});
async function manuallyCleanupStorage() {
try {
loading.cleanup = true;
await post('/internal/cleanup', {});
return addToast({
message: 'Cleanup done.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
loading.cleanup = false;
}
}
</script>
<div class="pb-4">
<div class="title">Hardware Details</div>
<div class="text-center p-8 ">
<div>
<div class="stat w-64">
<div class="stat-title">Total Memory</div>
<div class="stat-value">
<div class="w-full">
<div class="flex items-center">
<h1 class="title text-4xl">Hardware Details</h1>
<div class="flex space-x-4">
{#if $appSession.teamId === '0'}
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
>Cleanup Storage</button
>
<button
on:click={restartCoolify}
class:loading={loading.restart}
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
>
{/if}
</div>
</div>
<div class="divider" />
<div class="grid grid-flow-col gap-4 grid-rows-3 lg:grid-rows-1">
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
<div class="font-bold flex lg:justify-center">Memory</div>
<div class="stat">
<div class="stat-title">Total</div>
<div class="stat-value text-2xl">
{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div>
</div>
<div class="stat w-64">
<div class="stat-title">Used Memory</div>
<div class="stat-value">
<div class="stat">
<div class="stat-title">Used</div>
<div class="stat-value text-2xl">
{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div>
</div>
<div class="stat w-64">
<div class="stat-title">Free Memory</div>
<div class="stat-value">
<div class="stat">
<div class="stat-title">Free</div>
<div class="stat-value text-2xl">
{usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
</div>
</div>
</div>
<div class="py-10">
<div class="stat w-64">
<div class="stat-title">Total CPUs</div>
<div class="stat-value">
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
<div class="font-bold flex lg:justify-center">CPU</div>
<div class="stat">
<div class="stat-title">Total</div>
<div class="stat-value text-2xl">
{usage?.cpu.count}
</div>
</div>
<div class="stat w-64">
<div class="stat-title">CPU Usage</div>
<div class="stat-value">
<div class="stat">
<div class="stat-title">Usage</div>
<div class="stat-value text-2xl">
{usage?.cpu.usage}<span class="text-sm">%</span>
</div>
</div>
<div class="stat w-64">
<div class="stat">
<div class="stat-title">Load Average (5,10,30mins)</div>
<div class="stat-value">{usage?.cpu.load}</div>
<div class="stat-value text-2xl">{usage?.cpu.load}</div>
</div>
</div>
<div>
<div class="stat w-64">
<div class="stat-title">Total Disk</div>
<div class="stat-value">
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
<div class="font-bold flex lg:justify-center">Disk</div>
<div class="stat">
<div class="stat-title">Total</div>
<div class="stat-value text-2xl">
{usage?.disk.totalGb}<span class="text-sm">GB</span>
</div>
</div>
<div class="stat w-64">
<div class="stat-title">Used Disk</div>
<div class="stat-value">
<div class="stat">
<div class="stat-title">Used</div>
<div class="stat-value text-2xl">
{usage?.disk.usedGb}<span class="text-sm">GB</span>
</div>
</div>
<div class="stat w-64">
<div class="stat-title">Free Disk</div>
<div class="stat-value">{usage?.disk.freePercentage}<span class="text-sm">%</span></div>
<div class="stat">
<div class="stat-title">Free</div>
<div class="stat-value text-2xl">
{usage?.disk.freePercentage}<span class="text-sm">%</span>
</div>
</div>
</div>
</div>

View File

@ -2,79 +2,8 @@
export let isAbsolute = true;
</script>
<svg
viewBox="0 0 128 128"
class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-8 h-8'}
>
<g
><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#3A4D54"
d="M73.8 50.8h11.3v11.5h5.7c2.6 0 5.3-.5 7.8-1.3 1.2-.4 2.6-1 3.8-1.7-1.6-2.1-2.4-4.7-2.6-7.3-.3-3.5.4-8.1 2.8-10.8l1.2-1.4 1.4 1.1c3.6 2.9 6.5 6.8 7.1 11.4 4.3-1.3 9.3-1 13.1 1.2l1.5.9-.8 1.6c-3.2 6.2-9.9 8.2-16.4 7.8-9.8 24.3-31 35.8-56.8 35.8-13.3 0-25.5-5-32.5-16.8l-.1-.2-1-2.1c-2.4-5.2-3.1-10.9-2.6-16.6l.2-1.7h9.6v-11.4h11.3v-11.2h22.5v-11.3h13.5v22.5z"
/><path
fill="#00AADA"
d="M110.4 55.1c.8-5.9-3.6-10.5-6.4-12.7-3.1 3.6-3.6 13.2 1.3 17.2-2.8 2.4-8.5 4.7-14.5 4.7h-72.2c-.6 6.2.5 11.9 3 16.8l.8 1.5c.5.9 1.1 1.7 1.7 2.6 3 .2 5.7.3 8.2.2 4.9-.1 8.9-.7 12-1.7.5-.2.9.1 1.1.5.2.5-.1.9-.5 1.1-.4.1-.8.3-1.3.4-2.4.7-5 1.1-8.3 1.3h-.6000000000000001c-1.3.1-2.7.1-4.2.1-1.6 0-3.1 0-4.9-.1 6 6.8 15.4 10.8 27.2 10.8 25 0 46.2-11.1 55.5-35.9 6.7.7 13.1-1 16-6.7-4.5-2.7-10.5-1.8-13.9-.1z"
/><path
fill="#28B8EB"
d="M110.4 55.1c.8-5.9-3.6-10.5-6.4-12.7-3.1 3.6-3.6 13.2 1.3 17.2-2.8 2.4-8.5 4.7-14.5 4.7h-68c-.3 9.5 3.2 16.7 9.5 21 4.9-.1 8.9-.7 12-1.7.5-.2.9.1 1.1.5.2.5-.1.9-.5 1.1-.4.1-.8.3-1.3.4-2.4.7-5.2 1.2-8.5 1.4l-.1-.1c8.5 4.4 20.8 4.3 35-1.1 15.8-6.1 30.6-17.7 40.9-30.9-.2.1-.4.1-.5.2z"
/><path
fill="#028BB8"
d="M18.7 71.8c.4 3.3 1.4 6.4 2.9 9.3l.8 1.5c.5.9 1.1 1.7 1.7 2.6 3 .2 5.7.3 8.2.2 4.9-.1 8.9-.7 12-1.7.5-.2.9.1 1.1.5.2.5-.1.9-.5 1.1-.4.1-.8.3-1.3.4-2.4.7-5.2 1.2-8.5 1.4h-.4c-1.3.1-2.7.1-4.1.1-1.6 0-3.2 0-4.9-.1 6 6.8 15.5 10.8 27.3 10.8 21.4 0 40-8.1 50.8-26h-85.1v-.1z"
/><path
fill="#019BC6"
d="M23.5 71.8c1.3 5.8 4.3 10.4 8.8 13.5 4.9-.1 8.9-.7 12-1.7.5-.2.9.1 1.1.5.2.5-.1.9-.5 1.1-.4.1-.8.3-1.3.4-2.4.7-5.2 1.2-8.6 1.4 8.5 4.4 20.8 4.3 34.9-1.1 8.5-3.3 16.8-8.2 24.2-14.1h-70.6z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00ACD3"
d="M28.4 52.7h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zM39.6 41.5h9.8v9.8h-9.8v-9.8zm.9.8h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#23C2EE"
d="M39.6 52.7h9.8v9.8h-9.8v-9.8zm.9.8h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00ACD3"
d="M50.9 52.7h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#23C2EE"
d="M50.9 41.5h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zM62.2 52.7h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00ACD3"
d="M62.2 41.5h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#23C2EE"
d="M62.2 30.2h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00ACD3"
d="M73.5 52.7h9.8v9.8h-9.8v-9.8zm.8.8h.8v8.1h-.8v-8.1zm1.4 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1zm1.5 0h.8v8.1h-.8v-8.1z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#D4EEF1"
d="M48.8 78.3c1.5 0 2.7 1.2 2.7 2.7 0 1.5-1.2 2.7-2.7 2.7-1.5 0-2.7-1.2-2.7-2.7 0-1.5 1.2-2.7 2.7-2.7"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#3A4D54"
d="M48.8 79.1c.2 0 .5 0 .7.1-.2.1-.4.4-.4.7 0 .4.4.8.8.8.3 0 .6-.2.7-.4.1.2.1.5.1.7 0 1.1-.9 1.9-1.9 1.9-1.1 0-1.9-.9-1.9-1.9 0-1 .8-1.9 1.9-1.9M1.1 72.8h125.4c-2.7-.7-8.6-1.6-7.7-5.2-5 5.7-16.9 4-20 1.2-3.4 4.9-23 3-24.3-.8-4.2 5-17.3 5-21.5 0-1.4 3.8-21 5.7-24.3.8-3 2.8-15 4.5-20-1.2 1.1 3.5-4.9 4.5-7.6 5.2"
/><path
fill="#BFDBE0"
d="M56 97.8c-6.7-3.2-10.3-7.5-12.4-12.2-2.5.7-5.5 1.2-8.9 1.4-1.3.1-2.7.1-4.1.1-1.7 0-3.4 0-5.2-.1 6 6 13.6 10.7 27.5 10.8h3.1z"
/><path
fill="#D4EEF1"
d="M46.1 89.9c-.9-1.3-1.8-2.8-2.5-4.3-2.5.7-5.5 1.2-8.9 1.4 2.3 1.2 5.7 2.4 11.4 2.9z"
/></g
>
</svg>
<svg viewBox="0 0 128 128" class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-10 h-10'}>
<path d="M124.8 52.1c-4.3-2.5-10-2.8-14.8-1.4-.6-5.2-4-9.7-8-12.9l-1.6-1.3-1.4 1.6c-2.7 3.1-3.5 8.3-3.1 12.3.3 2.9 1.2 5.9 3 8.3-1.4.8-2.9 1.9-4.3 2.4-2.8 1-5.9 2-8.9 2H79V49H66V24H51v12H26v13H13v14H1.8l-.2 1.5c-.5 6.4.3 12.6 3 18.5l1.1 2.2.1.2c7.9 13.4 21.7 19 36.8 19 29.2 0 53.3-13.1 64.3-40.6 7.4.4 15-1.8 18.6-8.9l.9-1.8-1.6-1zM28 39h10v11H28V39zm13.1 44.2c0 1.7-1.4 3.1-3.1 3.1-1.7 0-3.1-1.4-3.1-3.1 0-1.7 1.4-3.1 3.1-3.1 1.7.1 3.1 1.4 3.1 3.1zM28 52h10v11H28V52zm-13 0h11v11H15V52zm27.7 50.2c-15.8-.1-24.3-5.4-31.3-12.4 2.1.1 4.1.2 5.9.2 1.6 0 3.2 0 4.7-.1 3.9-.2 7.3-.7 10.1-1.5 2.3 5.3 6.5 10.2 14 13.8h-3.4zM51 63H40V52h11v11zm0-13H40V39h11v11zm13 13H53V52h11v11zm0-13H53V39h11v11zm0-13H53V26h11v11zm13 26H66V52h11v11zM38.8 81.2c-.2-.1-.5-.2-.8-.2-1.2 0-2.2 1-2.2 2.2 0 1.2 1 2.2 2.2 2.2s2.2-1 2.2-2.2c0-.3-.1-.6-.2-.8-.2.3-.4.5-.8.5-.5 0-.9-.4-.9-.9.1-.4.3-.7.5-.8z" fill="#019BC6"></path>
</svg>

View File

@ -4,7 +4,7 @@
<svg
xmlns="http://www.w3.org/2000/svg"
class={isAbsolute ? 'w-14 h-14 absolute top-0 left-0 -m-5' : 'w-9 mx-auto'}
class={isAbsolute ? 'w-14 h-14 absolute top-0 left-0 -m-5' : 'w-9 h-9 mx-auto'}
viewBox="0 0 384 384"
fill="none"
>

View File

@ -5,7 +5,7 @@
<svg
viewBox="0 0 700 240"
xmlns="http://www.w3.org/2000/svg"
class={isAbsolute ? 'w-36 absolute top-0 left-0 -m-3 -mt-5' : 'w-28 mx-auto'}
class={isAbsolute ? 'w-36 absolute top-0 left-0 -m-3 -mt-5' : 'w-28 h-28 mx-auto'}
><path fill="#FDBC3D" d="m90.694 107.498-.981.39-20.608 8.23 6.332 6.547z" /><path
fill="#8EC63F"
d="M61.139 77.914 46.632 93 56.9 103.547c8.649-7.169 17.832-10.502 18.653-10.789L61.139 77.914z"

View File

@ -3,7 +3,7 @@
</script>
<svg
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate"

View File

@ -4,6 +4,6 @@
<img
alt="plausible logo"
class={isAbsolute ? 'w-9 absolute top-0 left-0 -m-4' : 'w-6 mx-auto'}
class={isAbsolute ? 'w-9 h-12 absolute top-0 left-0 -m-4' : 'w-6 h-8 mx-auto'}
src="/plausible.png"
/>

View File

@ -3,7 +3,6 @@
</script>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
@ -11,7 +10,7 @@
id="svg8"
version="1.1"
viewBox="0 0 92 92"
class={isAbsolute ? 'w-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 mx-auto'}
class={isAbsolute ? 'w-12 h-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 h-8 mx-auto'}
>
<defs id="defs2" />
<metadata id="metadata5">

View File

@ -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 @@
<form on:submit|preventDefault={() => handleDeploySubmit(true)}>
<button
type="submit"
disabled={$disabledButton || !isQueueActive}
class:hover:text-green-500={isQueueActive}
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2"
data-tip={$appSession.isAdmin
? isQueueActive
? 'Force Rebuild Application'
: 'Autoupdate inprogress. Cannot rebuild application.'
? 'Force Rebuild Application'
: 'You do not have permission to rebuild application.'}
>
<svg
@ -287,7 +282,7 @@
</button>
</form>
{:else}
<form on:submit|preventDefault={handleDeploySubmit}>
<form on:submit|preventDefault={() => handleDeploySubmit(false)}>
<button
type="submit"
disabled={$disabledButton}
@ -359,7 +354,7 @@
<button
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Secret"
data-tip="Secrets"
>
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@ -158,7 +158,6 @@
{build.type}
</div>
</div>
<div class="flex-1" />
<div class="w-48 text-center text-xs">
{#if build.status === 'running'}
@ -178,7 +177,7 @@
{#if !noMoreBuilds}
{#if buildCount > 5}
<div class="flex space-x-2">
<button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}
<button disabled={noMoreBuilds} class=" btn btn-sm w-full" on:click={loadMoreBuilds}
>{$t('application.build.load_more')}</button
>
</div>

View File

@ -36,10 +36,6 @@
import ServiceIcons from '$lib/components/svg/services/ServiceIcons.svelte';
import { dev } from '$app/env';
let loading = {
cleanup: false
};
let numberOfGetStatus = 0;
function getRndInteger(min: number, max: number) {
@ -75,280 +71,217 @@
numberOfGetStatus--;
}
}
async function manuallyCleanupStorage() {
try {
loading.cleanup = true;
await post('/internal/cleanup', {});
return addToast({
message: 'Cleanup done.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
loading.cleanup = false;
}
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div>
{#if $appSession.teamId === '0'}
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
>Cleanup Storage</button
>
{/if}
</div>
<div class="mt-10 pb-12 sm:pb-16">
<div class="mx-auto px-10">
<div class="flex flex-col justify-center xl:flex-row">
{#if applications.length > 0}
<div>
<div class="title">Resources</div>
<div class="flex items-start justify-center p-8">
<table class="rounded-none text-base">
<tbody>
{#each applications as application}
<tr>
<td class="space-x-2 items-center tracking-tight font-bold">
{#await getStatus(application)}
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
{:then status}
{#if status === 'Running'}
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
{:else}
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
{/if}
{/await}
<div class="inline-flex">{application.name}</div>
</td>
<td class="px-10 inline-flex">
<ApplicationsIcons {application} isAbsolute={false} />
</td>
<td class="px-10">
<div
class="badge badge-outline text-xs border-applications rounded text-white"
<div class="container lg:mx-auto lg:p-0 p-5">
{#if $appSession.teamId === '0'}
<Usage />
{/if}
<h1 class="title text-4xl mt-10">Applications</h1>
<div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4">
{#if applications.length > 0}
{#each applications as application}
<a class="no-underline mb-5" href={`/applications/${application.id}`}>
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-green-600 indicator">
{#await getStatus(application)}
<span class="indicator-item badge bg-yellow-500 badge-xs" />
{:then status}
{#if status === 'Running'}
<span class="indicator-item badge bg-success badge-xs" />
{:else}
<span class="indicator-item badge bg-error badge-xs" />
{/if}
{/await}
<div class="w-full flex flex-row">
<ApplicationsIcons {application} isAbsolute={false} />
<div class="w-full flex flex-col ml-5">
<h1 class="font-bold text-xl truncate">
{application.name}
{#if application.settings.isBot}
<span class="text-xs"> BOT</span>
{/if}
</h1>
<div class="h-10">
{#if application?.fqdn}
<h2>{application?.fqdn.replace('https://', '').replace('http://', '')}</h2>
{:else if !application.settings.isBot && !application?.fqdn}
<h2 class="text-red-500">Not configured</h2>
{/if}
</div>
<div class="flex justify-end items-end space-x-2 h-10">
{#if application.fqdn}
<a href={application.fqdn} target="_blank" class="icons hover:bg-green-500">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
Application
{#if application.settings.isBot}
| BOT
{/if}
</div></td
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg>
</a>
{/if}
{#if application.settings.isBot && application.exposePort}
<a
href={`http://${dev ? 'localhost' : settings.ipv4}:${application.exposePort}`}
target="_blank"
class="icons hover:bg-green-500"
>
<td class="flex justify-end">
{#if application.fqdn}
<a
href={application.fqdn}
target="_blank"
class="icons bg-transparent text-sm inline-flex"
><svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg></a
>
{/if}
{#if application.settings.isBot && application.exposePort}
<a
href={`http://${dev ? 'localhost' : settings.ipv4}:${
application.exposePort
}`}
target="_blank"
class="icons bg-transparent text-sm inline-flex"
><svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg></a
>
{/if}
<a
href={`/applications/${application.id}`}
class="icons bg-transparent text-sm inline-flex"
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect x="4" y="8" width="4" height="4" />
<line x1="6" y1="4" x2="6" y2="8" />
<line x1="6" y1="12" x2="6" y2="20" />
<rect x="10" y="14" width="4" height="4" />
<line x1="12" y1="4" x2="12" y2="14" />
<line x1="12" y1="18" x2="12" y2="20" />
<rect x="16" y="5" width="4" height="4" />
<line x1="18" y1="4" x2="18" y2="5" />
<line x1="18" y1="9" x2="18" y2="20" />
</svg>
</a>
</td>
</tr>
{/each}
{#each services as service}
<tr>
<td class="space-x-2 items-center tracking-tight font-bold">
{#await getStatus(service)}
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
{:then status}
{#if status === 'Running'}
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
{:else}
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
{/if}
{/await}
<div class="inline-flex">{service.name}</div>
</td>
<td class="px-10 inline-flex">
<ServiceIcons type={service.type} isAbsolute={false} />
</td>
<td class="px-10"
><div class="badge badge-outline text-xs border-services rounded text-white">
Service
</div>
</td>
<td class="flex justify-end">
{#if service.fqdn}
<a
href={service.fqdn}
target="_blank"
class="icons bg-transparent text-sm inline-flex"
><svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg></a
>
{/if}
<a
href={`/services/${service.id}`}
class="icons bg-transparent text-sm inline-flex"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect x="4" y="8" width="4" height="4" />
<line x1="6" y1="4" x2="6" y2="8" />
<line x1="6" y1="12" x2="6" y2="20" />
<rect x="10" y="14" width="4" height="4" />
<line x1="12" y1="4" x2="12" y2="14" />
<line x1="12" y1="18" x2="12" y2="20" />
<rect x="16" y="5" width="4" height="4" />
<line x1="18" y1="4" x2="18" y2="5" />
<line x1="18" y1="9" x2="18" y2="20" />
</svg>
</a>
</td>
</tr>
{/each}
{#each databases as database}
<tr>
<td class="space-x-2 items-center tracking-tight font-bold">
{#await getStatus(database)}
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
{:then status}
{#if status === 'Running'}
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
{:else}
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
{/if}
{/await}
<div class="inline-flex">{database.name}</div>
</td>
<td class="px-10 inline-flex">
<DatabaseIcons type={database.type} />
</td>
<td class="px-10">
<div class="badge badge-outline text-xs border-databases rounded text-white">
Database
</div>
</td>
<td class="flex justify-end">
<a
href={`/databases/${database.id}`}
class="icons bg-transparent text-sm inline-flex ml-11"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect x="4" y="8" width="4" height="4" />
<line x1="6" y1="4" x2="6" y2="8" />
<line x1="6" y1="12" x2="6" y2="20" />
<rect x="10" y="14" width="4" height="4" />
<line x1="12" y1="4" x2="12" y2="14" />
<line x1="12" y1="18" x2="12" y2="20" />
<rect x="16" y="5" width="4" height="4" />
<line x1="18" y1="4" x2="18" y2="5" />
<line x1="18" y1="9" x2="18" y2="20" />
</svg>
</a>
</td>
</tr>
{/each}
</tbody>
</table>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg>
</a>
{/if}
</div>
</div>
</div>
</div>
</div>
{:else if $appSession.teamId !== '0'}
<div class="text-center text-xl font-bold">Nothing is configured yet.</div>
{/if}
{#if $appSession.teamId === '0'}
<Usage />
{/if}
</div>
</a>
{/each}
{:else}
<h1 class="">Nothing is configured yet.</h1>
{/if}
</div>
<h1 class="title text-4xl mt-10">Services</h1>
<div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4">
{#if services.length > 0}
{#each services as service}
<a class="no-underline mb-5" href={`/services/${service.id}`}>
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-pink-600 indicator">
{#await getStatus(service)}
<span class="indicator-item badge bg-yellow-500 badge-xs" />
{:then status}
{#if status === 'Running'}
<span class="indicator-item badge bg-success badge-xs" />
{:else}
<span class="indicator-item badge bg-error badge-xs" />
{/if}
{/await}
<div class="w-full flex flex-row">
<ServiceIcons type={service.type} isAbsolute={false} />
<div class="w-full flex flex-col ml-5">
<h1 class="font-bold text-xl truncate">{service.name}</h1>
<div class="h-10">
{#if service?.fqdn}
<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2>
{:else}
<h2 class="text-red-500">Not configured</h2>
{/if}
</div>
<div class="flex justify-end items-end space-x-2 h-10">
{#if service.fqdn}
<a href={service.fqdn} target="_blank" class="icons hover:bg-pink-500">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg>
</a>
{/if}
</div>
</div>
</div>
</div>
</a>
{/each}
{:else}
<h1 class="">Nothing is configured yet.</h1>
{/if}
</div>
<h1 class="title text-4xl mt-10">Databases</h1>
<div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4 mb-32">
{#if databases.length > 0}
{#each databases as database}
<a class="no-underline mb-5" href={`/databases/${database.id}`}>
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-purple-500 indicator">
{#await getStatus(database)}
<span class="indicator-item badge bg-yellow-500 badge-xs" />
{:then status}
{#if status === 'Running'}
<span class="indicator-item badge bg-success badge-xs" />
{:else}
<span class="indicator-item badge bg-error badge-xs" />
{/if}
{/await}
<div class="w-full flex flex-row pt-2">
<DatabaseIcons type={database.type} isAbsolute={false} />
<div class="w-full flex flex-col ml-5">
<div class="h-10">
<h1 class="font-bold text-xl truncate">{database.name}</h1>
<div class="h-10">
{#if database?.version}
<h2>{database?.version}</h2>
{:else}
<h2 class="text-red-500">Not configured</h2>
{/if}
</div>
</div>
<div class="flex justify-end items-end space-x-2 h-10">
{#if database.settings.isPublic}
<div title="Public">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 "
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="9" />
<line x1="3.6" y1="9" x2="20.4" y2="9" />
<line x1="3.6" y1="15" x2="20.4" y2="15" />
<path d="M11.5 3a17 17 0 0 0 0 18" />
<path d="M12.5 3a17 17 0 0 1 0 18" />
</svg>
</div>
{/if}
</div>
</div>
</div>
</div>
</a>
{/each}
{:else}
<h1 class="">Nothing is configured yet.</h1>
{/if}
</div>
</div>

View File

@ -62,5 +62,5 @@ module.exports = {
scrollbar: ['dark'],
extend: {}
},
plugins: [require('tailwindcss-scrollbar'), require('daisyui')]
plugins: [require('tailwindcss-scrollbar'), require('daisyui'), require("@tailwindcss/typography")]
};

View File

@ -1,7 +1,7 @@
{
"name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.8.1",
"version": "3.8.2",
"license": "Apache-2.0",
"repository": "github:coollabsio/coolify",
"scripts": {

431
pnpm-lock.yaml generated
View File

@ -22,16 +22,17 @@ importers:
'@fastify/jwt': 6.3.2
'@fastify/static': 6.5.0
'@iarna/toml': 2.2.5
'@ladjs/graceful': 3.0.2
'@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 +53,8 @@ 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
p-throttle: 5.0.0
prettier: 2.7.1
prisma: 3.15.2
public-ip: 6.0.1
@ -63,7 +65,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
@ -71,12 +73,13 @@ importers:
'@fastify/jwt': 6.3.2
'@fastify/static': 6.5.0
'@iarna/toml': 2.2.5
'@ladjs/graceful': 3.0.2
'@prisma/client': 3.15.2_prisma@3.15.2
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
@ -92,16 +95,17 @@ 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
p-throttle: 5.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 +119,17 @@ 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
'@tailwindcss/typography': ^0.5.4
'@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 +138,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
@ -144,27 +149,28 @@ importers:
vite: 3.0.5
dependencies:
'@sveltejs/adapter-static': 1.0.0-next.39
'@tailwindcss/typography': 0.5.4_tailwindcss@3.1.8
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 +217,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'
@ -374,6 +380,14 @@ packages:
resolution: {integrity: sha512-hZere0rUga8kTzSTFbHREXpD9E/jwi94+B5RyLAmMIzl/w/EK1z7rFEnMHzPkU4AZkL42JWSsGXoV8LXMihybg==}
dev: false
/@ladjs/graceful/3.0.2:
resolution: {integrity: sha512-T4Z+0R0zgZfR32KIs3FEuH7oFSnhj3c+00wVtp07aeIl8PDfQGcXGB3C8SfOZ2EzPMRuIpIul5kHkVBdSTULXw==}
engines: {node: '>=14'}
dependencies:
lil-http-terminator: 1.2.2
p-is-promise: 3.0.0
dev: false
/@leichtgewicht/ip-codec/2.0.4:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
dev: false
@ -401,13 +415,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:
@ -513,6 +527,17 @@ packages:
defer-to-connect: 2.0.1
dev: false
/@tailwindcss/typography/0.5.4_tailwindcss@3.1.8:
resolution: {integrity: sha512-QEdg40EmGvE7kKoDei8zr5sf4D1pIayHj4R31bH3lX8x2BtTiR+jNejYPOkhbmy3DXgkMF9jC8xqNiGFAuL9Sg==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
tailwindcss: 3.1.8
dev: false
/@tsconfig/node10/1.0.8:
resolution: {integrity: sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==}
dev: false
@ -534,7 +559,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 +582,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 +593,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 +607,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 +627,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 +643,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 +653,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 +663,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 +681,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 +690,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 +704,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 +716,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 +734,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 +1964,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 +2115,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 +2875,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 +2901,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 +2927,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 +2947,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 +3047,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}
@ -4179,6 +3999,11 @@ packages:
set-cookie-parser: 2.4.8
dev: false
/lil-http-terminator/1.2.2:
resolution: {integrity: sha512-2n6gKJIKgPjy4JfSlwsQnAA7wK4SEA1cegqdsYwr7qObVfIHdELjDGjEcYWJanfF/u/mRzIT2WPqhpzC6R9pZw==}
engines: {node: '>=8'}
dev: false
/lilconfig/2.0.6:
resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==}
engines: {node: '>=10'}
@ -4254,6 +4079,10 @@ packages:
lodash._basetostring: 4.12.0
dev: false
/lodash.castarray/4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
dev: false
/lodash.includes/4.3.0:
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
dev: false
@ -4280,7 +4109,6 @@ packages:
/lodash.merge/4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
/lodash.omit/4.5.0:
resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==}
@ -4703,6 +4531,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'}
@ -4713,6 +4548,11 @@ packages:
engines: {node: '>=4'}
dev: false
/p-is-promise/3.0.0:
resolution: {integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==}
engines: {node: '>=8'}
dev: false
/p-limit/2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
@ -4754,12 +4594,16 @@ 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-throttle/5.0.0:
resolution: {integrity: sha512-iXBFjW4kP/5Ivw7uC9EDnj+/xo3pNn4Rws3zgMGPwXnWTv1M3P0LVdZxLrqRUI5JK0Fp3Du0bt6lCaVrI3WF7g==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/p-timeout/3.2.0:
@ -4769,11 +4613,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 +4793,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 +5590,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 +5827,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 +5846,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