feat: Coolify auto-updater
This commit is contained in:
parent
8290ee856f
commit
11d74c0c1f
@ -2,5 +2,7 @@ COOLIFY_APP_ID=
|
||||
COOLIFY_SECRET_KEY=12341234123412341234123412341234
|
||||
COOLIFY_DATABASE_URL=file:../db/dev.db
|
||||
COOLIFY_SENTRY_DSN=
|
||||
COOLIFY_IS_ON="docker"
|
||||
COOLIFY_WHITE_LABELED="false"
|
||||
COOLIFY_IS_ON=docker
|
||||
COOLIFY_WHITE_LABELED=false
|
||||
COOLIFY_WHITE_LABELED_ICON=
|
||||
COOLIFY_AUTO_UPDATE=false
|
@ -0,0 +1,22 @@
|
||||
-- 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,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "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;
|
@ -18,6 +18,7 @@ model Setting {
|
||||
proxyPassword String
|
||||
proxyUser String
|
||||
proxyHash String?
|
||||
isAutoUpdateEnabled Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
@ -50,6 +50,20 @@ async function main() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set auto-update based on env variable
|
||||
const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true';
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
if (settings) {
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
id: settings.id
|
||||
},
|
||||
data: {
|
||||
isAutoUpdateEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
main()
|
||||
.catch((e) => {
|
||||
|
@ -302,7 +302,9 @@
|
||||
"registration_allowed": "Registration allowed?",
|
||||
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
||||
"coolify_proxy_settings": "Coolify Proxy Settings",
|
||||
"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page."
|
||||
"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page.",
|
||||
"auto_update_enabled": "Auto update enabled?",
|
||||
"auto_update_enabled_explainer": "Enable automatic updates for Coolify."
|
||||
},
|
||||
"team": {
|
||||
"pending_invitations": "Pending invitations",
|
||||
|
39
src/lib/queues/autoUpdater.ts
Normal file
39
src/lib/queues/autoUpdater.ts
Normal file
@ -0,0 +1,39 @@
|
||||
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> {
|
||||
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) {
|
||||
console.log('Updating...');
|
||||
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 {
|
||||
console.log('Updating (not really in dev mode).');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('No update available.');
|
||||
}
|
||||
} else {
|
||||
console.log('Auto update is disabled.');
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ 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';
|
||||
|
||||
@ -34,19 +35,22 @@ const cron = async (): Promise<void> => {
|
||||
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',
|
||||
@ -98,11 +102,22 @@ const cron = async (): Promise<void> => {
|
||||
}
|
||||
);
|
||||
|
||||
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 } });
|
||||
await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||
};
|
||||
cron().catch((error) => {
|
||||
console.log('cron failed to start');
|
||||
|
@ -64,10 +64,14 @@ export const post: RequestHandler = async (event) => {
|
||||
};
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort } = await event.request.json();
|
||||
const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort, isAutoUpdateEnabled } =
|
||||
await event.request.json();
|
||||
try {
|
||||
const { id } = await db.listSettings();
|
||||
await db.prisma.setting.update({ where: { id }, data: { isRegistrationEnabled, dualCerts } });
|
||||
await db.prisma.setting.update({
|
||||
where: { id },
|
||||
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled }
|
||||
});
|
||||
if (fqdn) {
|
||||
await db.prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||
}
|
||||
|
@ -40,10 +40,9 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
import Language from './_Language.svelte';
|
||||
|
||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||
let dualCerts = settings.dualCerts;
|
||||
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||
|
||||
let minPort = settings.minPort;
|
||||
let maxPort = settings.maxPort;
|
||||
@ -76,7 +75,10 @@
|
||||
if (name === 'dualCerts') {
|
||||
dualCerts = !dualCerts;
|
||||
}
|
||||
await post(`/settings.json`, { isRegistrationEnabled, dualCerts });
|
||||
if (name === 'isAutoUpdateEnabled') {
|
||||
isAutoUpdateEnabled = !isAutoUpdateEnabled;
|
||||
}
|
||||
await post(`/settings.json`, { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled });
|
||||
return toast.push(t.get('application.settings_saved'));
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@ -192,6 +194,14 @@
|
||||
on:click={() => changeSettings('isRegistrationEnabled')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={isAutoUpdateEnabled}
|
||||
title={$t('setting.auto_update_enabled')}
|
||||
description={$t('setting.auto_update_enabled_explainer')}
|
||||
on:click={() => changeSettings('isAutoUpdateEnabled')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="flex space-x-1 pt-6 font-bold">
|
||||
|
Loading…
x
Reference in New Issue
Block a user