217 lines
8.9 KiB
TypeScript
217 lines
8.9 KiB
TypeScript
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);
|
|
})();
|