mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-22 12:34:26 +00:00
Merged upstream and fixed expose port implementation
This commit is contained in:
42
src/lib/queues/autoUpdater.ts
Normal file
42
src/lib/queues/autoUpdater.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { prisma } from '$lib/database';
|
||||
import { buildQueue } from '.';
|
||||
import got from 'got';
|
||||
import { asyncExecShell, version } from '$lib/common';
|
||||
import compare from 'compare-versions';
|
||||
import { dev } from '$app/env';
|
||||
|
||||
export default async function (): Promise<void> {
|
||||
try {
|
||||
const currentVersion = version;
|
||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||
if (isAutoUpdateEnabled) {
|
||||
const versions = await got
|
||||
.get(
|
||||
`https://get.coollabs.io/versions.json?appId=${process.env['COOLIFY_APP_ID']}&version=${currentVersion}`
|
||||
)
|
||||
.json();
|
||||
const latestVersion = versions['coolify'].main.version;
|
||||
const isUpdateAvailable = compare(latestVersion, currentVersion);
|
||||
if (isUpdateAvailable === 1) {
|
||||
const activeCount = await buildQueue.getActiveCount();
|
||||
if (activeCount === 0) {
|
||||
if (!dev) {
|
||||
await buildQueue.pause();
|
||||
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 coolify-redis && docker rm coolify coolify-redis && docker compose up -d --force-recreate"`
|
||||
);
|
||||
} else {
|
||||
await buildQueue.pause();
|
||||
console.log('Updating (not really in dev mode).');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
await buildQueue.resume();
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
@@ -20,28 +20,22 @@ import {
|
||||
setDefaultConfiguration
|
||||
} from '$lib/buildPacks/common';
|
||||
import yaml from 'js-yaml';
|
||||
import type { Job } from 'bullmq';
|
||||
import type { BuilderJob } from '$lib/types/builderJob';
|
||||
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export default async function (job) {
|
||||
let {
|
||||
export default async function (job: Job<BuilderJob, void, string>): Promise<void> {
|
||||
const {
|
||||
id: applicationId,
|
||||
repository,
|
||||
branch,
|
||||
buildPack,
|
||||
name,
|
||||
destinationDocker,
|
||||
destinationDockerId,
|
||||
gitSource,
|
||||
build_id: buildId,
|
||||
configHash,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
fqdn,
|
||||
baseDirectory,
|
||||
publishDirectory,
|
||||
projectId,
|
||||
secrets,
|
||||
phpModules,
|
||||
@@ -52,7 +46,21 @@ export default async function (job) {
|
||||
persistentStorage,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
denoOptions,
|
||||
exposePort
|
||||
} = job.data;
|
||||
let {
|
||||
branch,
|
||||
buildPack,
|
||||
port,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
baseDirectory,
|
||||
publishDirectory,
|
||||
dockerFileLocation,
|
||||
denoMainFile
|
||||
} = job.data;
|
||||
const { debug } = settings;
|
||||
|
||||
@@ -68,7 +76,7 @@ export default async function (job) {
|
||||
});
|
||||
let imageId = applicationId;
|
||||
let domain = getDomain(fqdn);
|
||||
let volumes =
|
||||
const volumes =
|
||||
persistentStorage?.map((storage) => {
|
||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${
|
||||
buildPack !== 'docker' ? '/app' : ''
|
||||
@@ -103,8 +111,10 @@ export default async function (job) {
|
||||
buildCommand = configuration.buildCommand;
|
||||
publishDirectory = configuration.publishDirectory;
|
||||
baseDirectory = configuration.baseDirectory;
|
||||
dockerFileLocation = configuration.dockerFileLocation;
|
||||
denoMainFile = configuration.denoMainFile;
|
||||
|
||||
let commit = await importers[gitSource.type]({
|
||||
const commit = await importers[gitSource.type]({
|
||||
applicationId,
|
||||
debug,
|
||||
workdir,
|
||||
@@ -179,6 +189,7 @@ export default async function (job) {
|
||||
}
|
||||
if (!imageFound || deployNeeded) {
|
||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId);
|
||||
console.log(exposePort ? `${exposePort}:${port}` : port);
|
||||
if (buildpacks[buildPack])
|
||||
await buildpacks[buildPack]({
|
||||
buildId,
|
||||
@@ -197,7 +208,7 @@ export default async function (job) {
|
||||
tag,
|
||||
workdir,
|
||||
docker,
|
||||
port,
|
||||
port: exposePort ? `${exposePort}:${port}` : port,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -206,15 +217,16 @@ export default async function (job) {
|
||||
phpModules,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions
|
||||
});
|
||||
else {
|
||||
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
|
||||
throw new Error(`Build pack ${buildPack} not found.`);
|
||||
}
|
||||
deployNeeded = true;
|
||||
} else {
|
||||
deployNeeded = false;
|
||||
await saveBuildLog({ line: 'Nothing changed.', buildId, applicationId });
|
||||
}
|
||||
|
||||
@@ -250,7 +262,7 @@ export default async function (job) {
|
||||
repository,
|
||||
branch,
|
||||
projectId,
|
||||
port,
|
||||
port: exposePort ? `${exposePort}:${port}` : port,
|
||||
commit,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
@@ -282,10 +294,21 @@ export default async function (job) {
|
||||
volumes,
|
||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||
networks: [docker.network],
|
||||
ports: exposePort ? [`${exposePort}:${port}`] : [],
|
||||
labels,
|
||||
depends_on: [],
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
// logging: {
|
||||
// driver: 'fluentd',
|
||||
// },
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { dev } from '$app/env';
|
||||
import { asyncExecShell, getEngine, version } from '$lib/common';
|
||||
import { prisma } from '$lib/database';
|
||||
import { defaultProxyImageHttp, defaultProxyImageTcp } from '$lib/haproxy';
|
||||
export default async function () {
|
||||
export default async function (): Promise<void> {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||
for (const destinationDocker of destinationDockers) {
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const engines = [...new Set(destinationDockers.map(({ engine }) => engine))];
|
||||
for (const engine of engines) {
|
||||
const host = getEngine(engine);
|
||||
// Cleanup old coolify images
|
||||
try {
|
||||
let { stdout: images } = await asyncExecShell(
|
||||
@@ -16,56 +15,23 @@ export default async function () {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker rmi -f ${images}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f --filter "until=2h"`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
// Cleanup old images older than a day
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
// Tagging images with labels
|
||||
// try {
|
||||
// const images = [
|
||||
// `coollabsio/${defaultProxyImageTcp}`,
|
||||
// `coollabsio/${defaultProxyImageHttp}`,
|
||||
// 'certbot/certbot:latest',
|
||||
// 'node:16.14.0-alpine',
|
||||
// 'alpine:latest',
|
||||
// 'nginx:stable-alpine',
|
||||
// 'node:lts',
|
||||
// 'php:apache',
|
||||
// 'rust:latest'
|
||||
// ];
|
||||
// for (const image of images) {
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image inspect ${image}`);
|
||||
// } catch (error) {
|
||||
// await asyncExecShell(
|
||||
// `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -`
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {}
|
||||
// if (!dev) {
|
||||
// // Cleanup images that are not managed by coolify
|
||||
// try {
|
||||
// await asyncExecShell(
|
||||
// `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f`
|
||||
// );
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// // Cleanup old images >3 days
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`);
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as Bullmq from 'bullmq';
|
||||
import { default as ProdBullmq, Job, QueueEvents, QueueScheduler } from 'bullmq';
|
||||
import cuid from 'cuid';
|
||||
import { default as ProdBullmq, QueueScheduler } from 'bullmq';
|
||||
import { dev } from '$app/env';
|
||||
import { prisma } from '$lib/database';
|
||||
|
||||
@@ -8,8 +7,10 @@ import builder from './builder';
|
||||
import logger from './logger';
|
||||
import cleanup from './cleanup';
|
||||
import proxy from './proxy';
|
||||
import proxyTcpHttp from './proxyTcpHttp';
|
||||
import ssl from './ssl';
|
||||
import sslrenewal from './sslrenewal';
|
||||
import autoUpdater from './autoUpdater';
|
||||
|
||||
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||
|
||||
@@ -28,22 +29,28 @@ const connectionOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
const cron = async () => {
|
||||
const cron = async (): Promise<void> => {
|
||||
new QueueScheduler('proxy', connectionOptions);
|
||||
new QueueScheduler('proxyTcpHttp', connectionOptions);
|
||||
new QueueScheduler('cleanup', connectionOptions);
|
||||
new QueueScheduler('ssl', connectionOptions);
|
||||
new QueueScheduler('sslRenew', connectionOptions);
|
||||
new QueueScheduler('autoUpdater', connectionOptions);
|
||||
|
||||
const queue = {
|
||||
proxy: new Queue('proxy', { ...connectionOptions }),
|
||||
proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }),
|
||||
cleanup: new Queue('cleanup', { ...connectionOptions }),
|
||||
ssl: new Queue('ssl', { ...connectionOptions }),
|
||||
sslRenew: new Queue('sslRenew', { ...connectionOptions })
|
||||
sslRenew: new Queue('sslRenew', { ...connectionOptions }),
|
||||
autoUpdater: new Queue('autoUpdater', { ...connectionOptions })
|
||||
};
|
||||
await queue.proxy.drain();
|
||||
await queue.proxyTcpHttp.drain();
|
||||
await queue.cleanup.drain();
|
||||
await queue.ssl.drain();
|
||||
await queue.sslRenew.drain();
|
||||
await queue.autoUpdater.drain();
|
||||
|
||||
new Worker(
|
||||
'proxy',
|
||||
@@ -55,6 +62,16 @@ const cron = async () => {
|
||||
}
|
||||
);
|
||||
|
||||
new Worker(
|
||||
'proxyTcpHttp',
|
||||
async () => {
|
||||
await proxyTcpHttp();
|
||||
},
|
||||
{
|
||||
...connectionOptions
|
||||
}
|
||||
);
|
||||
|
||||
new Worker(
|
||||
'ssl',
|
||||
async () => {
|
||||
@@ -85,22 +102,22 @@ const cron = async () => {
|
||||
}
|
||||
);
|
||||
|
||||
new Worker(
|
||||
'autoUpdater',
|
||||
async () => {
|
||||
await autoUpdater();
|
||||
},
|
||||
{
|
||||
...connectionOptions
|
||||
}
|
||||
);
|
||||
|
||||
await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } });
|
||||
await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
|
||||
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
|
||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
|
||||
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
|
||||
const events = {
|
||||
proxy: new QueueEvents('proxy', { ...connectionOptions }),
|
||||
ssl: new QueueEvents('ssl', { ...connectionOptions })
|
||||
};
|
||||
|
||||
events.proxy.on('completed', (data) => {
|
||||
// console.log(data)
|
||||
});
|
||||
events.ssl.on('completed', (data) => {
|
||||
// console.log(data)
|
||||
});
|
||||
await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||
};
|
||||
cron().catch((error) => {
|
||||
console.log('cron failed to start');
|
||||
@@ -113,6 +130,9 @@ const buildWorker = new Worker(buildQueueName, async (job) => await builder(job)
|
||||
concurrency: 1,
|
||||
...connectionOptions
|
||||
});
|
||||
buildQueue.resume().catch((err) => {
|
||||
console.log('Build queue failed to resume!', err);
|
||||
});
|
||||
|
||||
buildWorker.on('completed', async (job: Bullmq.Job) => {
|
||||
try {
|
||||
@@ -121,7 +141,6 @@ buildWorker.on('completed', async (job: Bullmq.Job) => {
|
||||
setTimeout(async () => {
|
||||
await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'success' } });
|
||||
}, 1234);
|
||||
console.log(error);
|
||||
} finally {
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}/${job.data.build_id}`;
|
||||
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
||||
@@ -137,7 +156,6 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
||||
setTimeout(async () => {
|
||||
await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'failed' } });
|
||||
}, 1234);
|
||||
console.log(error);
|
||||
} finally {
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}`;
|
||||
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { prisma } from '$lib/database';
|
||||
import { dev } from '$app/env';
|
||||
import type { Job } from 'bullmq';
|
||||
|
||||
export default async function (job) {
|
||||
export default async function (job: Job): Promise<void> {
|
||||
const { line, applicationId, buildId } = job.data;
|
||||
if (dev) console.debug(`[${applicationId}] ${line}`);
|
||||
await prisma.buildLog.create({ data: { line, buildId, time: Number(job.id), applicationId } });
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { configureHAProxy } from '$lib/haproxy/configuration';
|
||||
|
||||
export default async function () {
|
||||
export default async function (): Promise<void | {
|
||||
status: number;
|
||||
body: { message: string; error: string };
|
||||
}> {
|
||||
try {
|
||||
return await configureHAProxy();
|
||||
} catch (error) {
|
||||
|
||||
55
src/lib/queues/proxyTcpHttp.ts
Normal file
55
src/lib/queues/proxyTcpHttp.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ErrorHandler, generateDatabaseConfiguration, prisma } from '$lib/database';
|
||||
import { startCoolifyProxy, startHttpProxy, startTcpProxy } from '$lib/haproxy';
|
||||
|
||||
export default async function (): Promise<void | {
|
||||
status: number;
|
||||
body: { message: string; error: string };
|
||||
}> {
|
||||
try {
|
||||
// Coolify Proxy
|
||||
const localDocker = await prisma.destinationDocker.findFirst({
|
||||
where: { engine: '/var/run/docker.sock' }
|
||||
});
|
||||
if (localDocker && localDocker.isCoolifyProxyUsed) {
|
||||
await startCoolifyProxy('/var/run/docker.sock');
|
||||
}
|
||||
// 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) {
|
||||
const { privatePort } = generateDatabaseConfiguration(database);
|
||||
await startTcpProxy(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) {
|
||||
await startTcpProxy(destinationDocker, `${id}-ftp`, ftpPublicPort, 22);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
await startHttpProxy(destinationDocker, id, publicPort, 9000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return ErrorHandler(error.response?.body || error);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generateSSLCerts } from '$lib/letsencrypt';
|
||||
|
||||
export default async function () {
|
||||
export default async function (): Promise<void> {
|
||||
try {
|
||||
return await generateSSLCerts();
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { asyncExecShell } from '$lib/common';
|
||||
import { reloadHaproxy } from '$lib/haproxy';
|
||||
|
||||
export default async function () {
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew`
|
||||
);
|
||||
await reloadHaproxy('unix:///var/run/docker.sock');
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
export default async function (): Promise<void> {
|
||||
await asyncExecShell(
|
||||
`docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew`
|
||||
);
|
||||
await reloadHaproxy('unix:///var/run/docker.sock');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user