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_SECRET_KEY=12341234123412341234123412341234
|
||||||
COOLIFY_DATABASE_URL=file:../db/dev.db
|
COOLIFY_DATABASE_URL=file:../db/dev.db
|
||||||
COOLIFY_SENTRY_DSN=
|
COOLIFY_SENTRY_DSN=
|
||||||
COOLIFY_IS_ON="docker"
|
COOLIFY_IS_ON=docker
|
||||||
COOLIFY_WHITE_LABELED="false"
|
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
|
proxyPassword String
|
||||||
proxyUser String
|
proxyUser String
|
||||||
proxyHash String?
|
proxyHash String?
|
||||||
|
isAutoUpdateEnabled Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
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()
|
main()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
@ -302,7 +302,9 @@
|
|||||||
"registration_allowed": "Registration allowed?",
|
"registration_allowed": "Registration allowed?",
|
||||||
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
||||||
"coolify_proxy_settings": "Coolify Proxy Settings",
|
"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": {
|
"team": {
|
||||||
"pending_invitations": "Pending invitations",
|
"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 proxyTcpHttp from './proxyTcpHttp';
|
||||||
import ssl from './ssl';
|
import ssl from './ssl';
|
||||||
import sslrenewal from './sslrenewal';
|
import sslrenewal from './sslrenewal';
|
||||||
|
import autoUpdater from './autoUpdater';
|
||||||
|
|
||||||
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||||
|
|
||||||
@ -34,19 +35,22 @@ const cron = async (): Promise<void> => {
|
|||||||
new QueueScheduler('cleanup', connectionOptions);
|
new QueueScheduler('cleanup', connectionOptions);
|
||||||
new QueueScheduler('ssl', connectionOptions);
|
new QueueScheduler('ssl', connectionOptions);
|
||||||
new QueueScheduler('sslRenew', connectionOptions);
|
new QueueScheduler('sslRenew', connectionOptions);
|
||||||
|
new QueueScheduler('autoUpdater', connectionOptions);
|
||||||
|
|
||||||
const queue = {
|
const queue = {
|
||||||
proxy: new Queue('proxy', { ...connectionOptions }),
|
proxy: new Queue('proxy', { ...connectionOptions }),
|
||||||
proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }),
|
proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }),
|
||||||
cleanup: new Queue('cleanup', { ...connectionOptions }),
|
cleanup: new Queue('cleanup', { ...connectionOptions }),
|
||||||
ssl: new Queue('ssl', { ...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.proxy.drain();
|
||||||
await queue.proxyTcpHttp.drain();
|
await queue.proxyTcpHttp.drain();
|
||||||
await queue.cleanup.drain();
|
await queue.cleanup.drain();
|
||||||
await queue.ssl.drain();
|
await queue.ssl.drain();
|
||||||
await queue.sslRenew.drain();
|
await queue.sslRenew.drain();
|
||||||
|
await queue.autoUpdater.drain();
|
||||||
|
|
||||||
new Worker(
|
new Worker(
|
||||||
'proxy',
|
'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.proxy.add('proxy', {}, { repeat: { every: 10000 } });
|
||||||
await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
|
await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
|
||||||
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
|
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
|
||||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
|
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
|
||||||
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||||
|
await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||||
};
|
};
|
||||||
cron().catch((error) => {
|
cron().catch((error) => {
|
||||||
console.log('cron failed to start');
|
console.log('cron failed to start');
|
||||||
|
@ -64,10 +64,14 @@ export const post: RequestHandler = async (event) => {
|
|||||||
};
|
};
|
||||||
if (status === 401) return { status, body };
|
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 {
|
try {
|
||||||
const { id } = await db.listSettings();
|
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) {
|
if (fqdn) {
|
||||||
await db.prisma.setting.update({ where: { id }, data: { fqdn } });
|
await db.prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,9 @@
|
|||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
|
||||||
import Language from './_Language.svelte';
|
|
||||||
|
|
||||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||||
let dualCerts = settings.dualCerts;
|
let dualCerts = settings.dualCerts;
|
||||||
|
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||||
|
|
||||||
let minPort = settings.minPort;
|
let minPort = settings.minPort;
|
||||||
let maxPort = settings.maxPort;
|
let maxPort = settings.maxPort;
|
||||||
@ -76,7 +75,10 @@
|
|||||||
if (name === 'dualCerts') {
|
if (name === 'dualCerts') {
|
||||||
dualCerts = !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'));
|
return toast.push(t.get('application.settings_saved'));
|
||||||
} catch ({ error }) {
|
} catch ({ error }) {
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
@ -192,6 +194,14 @@
|
|||||||
on:click={() => changeSettings('isRegistrationEnabled')}
|
on:click={() => changeSettings('isRegistrationEnabled')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="flex space-x-1 pt-6 font-bold">
|
<div class="flex space-x-1 pt-6 font-bold">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user