commit
ef40f7349e
@ -3,6 +3,7 @@
|
||||
"description": "Coolify's Fastify API",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"db:generate":"prisma generate",
|
||||
"db:push": "prisma db push && prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"db:studio": "prisma studio",
|
||||
@ -11,7 +12,7 @@
|
||||
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --minify=true --platform=node --outdir=build --format=cjs",
|
||||
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .",
|
||||
"start": "NODE_ENV=production npx -y prisma migrate deploy && npx prisma generate && npx prisma db seed && node index.js"
|
||||
"start": "NODE_ENV=production pnpm prisma migrate deploy && pnpm prisma generate && pnpm prisma db seed && node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@breejs/ts-worker": "2.0.0",
|
||||
@ -25,6 +26,7 @@
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@ladjs/graceful": "3.0.2",
|
||||
"@prisma/client": "4.4.0",
|
||||
"prisma": "4.4.0",
|
||||
"axios": "0.27.2",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bree": "9.1.2",
|
||||
@ -66,7 +68,7 @@
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"nodemon": "2.0.20",
|
||||
"prettier": "2.7.1",
|
||||
"prisma": "4.4.0",
|
||||
|
||||
"rimraf": "3.0.2",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"typescript": "4.8.4"
|
||||
|
@ -10,6 +10,7 @@ import { asyncExecShell, createRemoteEngineConfiguration, getDomain, isDev, list
|
||||
import { scheduler } from './lib/scheduler';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import Graceful from '@ladjs/graceful'
|
||||
import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
|
||||
declare module 'fastify' {
|
||||
interface FastifyInstance {
|
||||
config: {
|
||||
@ -27,7 +28,8 @@ declare module 'fastify' {
|
||||
|
||||
const port = isDev ? 3001 : 3000;
|
||||
const host = '0.0.0.0';
|
||||
prisma.setting.findFirst().then(async (settings) => {
|
||||
(async () => {
|
||||
const settings = await prisma.setting.findFirst()
|
||||
const fastify = Fastify({
|
||||
logger: settings?.isAPIDebuggingEnabled || false,
|
||||
trustProxy: true
|
||||
@ -117,11 +119,8 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
// console.log('not allowed', request.headers.host)
|
||||
}
|
||||
})
|
||||
fastify.listen({ port, host }, async (err: any, address: any) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
await fastify.listen({ port, host })
|
||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||
await initServer();
|
||||
|
||||
@ -140,12 +139,12 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
// autoUpdater
|
||||
setInterval(async () => {
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:autoUpdater")
|
||||
}, isDev ? 5000 : 60000 * 15)
|
||||
}, 60000 * 15)
|
||||
|
||||
// cleanupStorage
|
||||
setInterval(async () => {
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
|
||||
}, isDev ? 6000 : 60000 * 10)
|
||||
}, 60000 * 10)
|
||||
|
||||
// checkProxies and checkFluentBit
|
||||
setInterval(async () => {
|
||||
@ -162,19 +161,28 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
getIPAddress(),
|
||||
configureRemoteDockers(),
|
||||
])
|
||||
});
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
})();
|
||||
|
||||
|
||||
async function getIPAddress() {
|
||||
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
||||
try {
|
||||
const settings = await listSettings();
|
||||
if (!settings.ipv4) {
|
||||
console.log(`Getting public IPv4 address...`);
|
||||
const ipv4 = await publicIpv4({ timeout: 2000 })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } })
|
||||
}
|
||||
|
||||
if (!settings.ipv6) {
|
||||
console.log(`Getting public IPv6 address...`);
|
||||
const ipv6 = await publicIpv6({ timeout: 2000 })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
||||
}
|
||||
@ -183,6 +191,7 @@ async function getIPAddress() {
|
||||
}
|
||||
async function initServer() {
|
||||
try {
|
||||
console.log(`Initializing server...`);
|
||||
await asyncExecShell(`docker network create --attachable coolify`);
|
||||
} catch (error) { }
|
||||
try {
|
||||
@ -196,6 +205,7 @@ async function getArch() {
|
||||
try {
|
||||
const settings = await prisma.setting.findFirst({})
|
||||
if (settings && !settings.arch) {
|
||||
console.log(`Getting architecture...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } })
|
||||
}
|
||||
} catch (error) { }
|
||||
@ -207,9 +217,13 @@ async function configureRemoteDockers() {
|
||||
where: { remoteVerified: true, remoteEngine: true }
|
||||
});
|
||||
if (remoteDocker.length > 0) {
|
||||
console.log(`Verifying Remote Docker Engines...`);
|
||||
for (const docker of remoteDocker) {
|
||||
await createRemoteEngineConfiguration(docker.id)
|
||||
console.log('Verifying:', docker.remoteIpAddress)
|
||||
await verifyRemoteDockerEngineFn(docker.id);
|
||||
}
|
||||
}
|
||||
} catch (error) { }
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import { scheduler } from './scheduler';
|
||||
import { supportedServiceTypesAndVersions } from './services/supportedVersions';
|
||||
import { includeServices } from './services/common';
|
||||
|
||||
export const version = '3.10.13';
|
||||
export const version = '3.10.14';
|
||||
export const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
const algorithm = 'aes-256-ctr';
|
||||
|
@ -919,7 +919,7 @@ async function startMeilisearchService(request: FastifyRequest<ServiceStartStop>
|
||||
const config = {
|
||||
meilisearch: {
|
||||
image: `${image}:${version}`,
|
||||
volumes: [`${id}-datams:/data.ms`],
|
||||
volumes: [`${id}-datams:/meili_data/data.ms`, `${id}-data:/meili_data `],
|
||||
environmentVariables: {
|
||||
MEILI_MASTER_KEY: masterKey
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export const supportedServiceTypesAndVersions = [
|
||||
},
|
||||
{
|
||||
name: 'wordpress',
|
||||
fancyName: 'Wordpress',
|
||||
fancyName: 'WordPress',
|
||||
baseImage: 'wordpress',
|
||||
images: ['bitnami/mysql:5.7'],
|
||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
||||
@ -255,4 +255,4 @@ export const supportedServiceTypesAndVersions = [
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
];
|
||||
];
|
||||
|
@ -2,12 +2,13 @@ import { FastifyPluginAsync } from 'fastify';
|
||||
import { errorHandler, listSettings, version } from '../../../../lib/common';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/', async () => {
|
||||
fastify.get('/', async (request) => {
|
||||
const teamId = request.user?.teamId;
|
||||
const settings = await listSettings()
|
||||
try {
|
||||
return {
|
||||
ipv4: settings.ipv4,
|
||||
ipv6: settings.ipv6,
|
||||
ipv4: teamId ? settings.ipv4 : 'nope',
|
||||
ipv6: teamId ? settings.ipv6 : 'nope',
|
||||
version,
|
||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||
|
@ -4,7 +4,7 @@ import sshConfig from 'ssh-config'
|
||||
import fs from 'fs/promises'
|
||||
import os from 'os';
|
||||
|
||||
import { asyncExecShell, createRemoteEngineConfiguration, decrypt, errorHandler, executeDockerCmd, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||
import { asyncExecShell, createRemoteEngineConfiguration, decrypt, errorHandler, executeDockerCmd, executeSSHCmd, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||
import { checkContainer } from '../../../../lib/docker';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
@ -202,25 +202,58 @@ export async function assignSSHKey(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
export async function verifyRemoteDockerEngineFn(id: string) {
|
||||
await createRemoteEngineConfiguration(id);
|
||||
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||
if (!stdout) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
}
|
||||
const { stdout: coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
||||
if (!coolifyNetwork) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
||||
}
|
||||
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
|
||||
try {
|
||||
const { id } = request.params;
|
||||
await createRemoteEngineConfiguration(id);
|
||||
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||
if (!stdout) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
}
|
||||
const { stdout: coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
||||
if (!coolifyNetwork) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
||||
}
|
||||
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||
return reply.code(201).send()
|
||||
let daemonJsonParsed = JSON.parse(daemonJson);
|
||||
let isUpdated = false;
|
||||
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
||||
isUpdated = true;
|
||||
daemonJsonParsed['live-restore'] = true
|
||||
|
||||
}
|
||||
if (!daemonJsonParsed?.features?.buildkit) {
|
||||
isUpdated = true;
|
||||
daemonJsonParsed.features = {
|
||||
buildkit: true
|
||||
}
|
||||
}
|
||||
if (isUpdated) {
|
||||
await executeSSHCmd({ dockerId: id, command: `echo '${JSON.stringify(daemonJsonParsed)}' > /etc/docker/daemon.json` });
|
||||
await executeSSHCmd({ dockerId: id, command: `systemctl restart docker` });
|
||||
}
|
||||
} catch (error) {
|
||||
const daemonJsonParsed = {
|
||||
"live-restore": true,
|
||||
"features": {
|
||||
"buildkit": true
|
||||
}
|
||||
}
|
||||
await executeSSHCmd({ dockerId: id, command: `echo '${JSON.stringify(daemonJsonParsed)}' > /etc/docker/daemon.json` });
|
||||
await executeSSHCmd({ dockerId: id, command: `systemctl restart docker` });
|
||||
} finally {
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||
}
|
||||
}
|
||||
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
const { id } = request.params;
|
||||
try {
|
||||
await verifyRemoteDockerEngineFn(id);
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } })
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,16 @@ export async function listServers(request: FastifyRequest) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const teamId = request.user.teamId;
|
||||
const servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } }}, distinct: ['remoteIpAddress', 'engine'] })
|
||||
let servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] })
|
||||
servers = servers.filter((server) => {
|
||||
if (server.remoteEngine) {
|
||||
if (server.remoteVerified) {
|
||||
return server
|
||||
}
|
||||
} else {
|
||||
return server
|
||||
}
|
||||
})
|
||||
return {
|
||||
servers
|
||||
}
|
||||
@ -78,7 +87,7 @@ export async function showUsage(request: FastifyRequest) {
|
||||
freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100
|
||||
},
|
||||
cpu: {
|
||||
load: [0,0,0],
|
||||
load: [0, 0, 0],
|
||||
usage: cpuUsage,
|
||||
count: cpus
|
||||
},
|
||||
|
@ -175,24 +175,23 @@
|
||||
disabled={loading.save}
|
||||
>{$t('forms.save')}
|
||||
</button>
|
||||
{#if !destination.remoteVerified}
|
||||
<button
|
||||
disabled={loading.verify}
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.verify}
|
||||
on:click|preventDefault|stopPropagation={verifyRemoteDocker}
|
||||
>Verify Remote Docker Engine</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.restart}
|
||||
class:bg-error={!loading.restart}
|
||||
disabled={loading.restart}
|
||||
on:click|preventDefault={forceRestartProxy}
|
||||
>{$t('destination.force_restart_proxy')}</button
|
||||
>
|
||||
{/if}
|
||||
<button
|
||||
disabled={loading.verify}
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.verify}
|
||||
on:click|preventDefault|stopPropagation={verifyRemoteDocker}
|
||||
>{!destination.remoteVerified
|
||||
? 'Verify Remote Docker Engine'
|
||||
: 'Check Remote Docker Engine'}</button
|
||||
>
|
||||
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.restart}
|
||||
class:bg-error={!loading.restart}
|
||||
disabled={loading.restart}
|
||||
on:click|preventDefault={forceRestartProxy}>{$t('destination.force_restart_proxy')}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10 ">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "3.10.13",
|
||||
"version": "3.10.14",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "github:coollabsio/coolify",
|
||||
"scripts": {
|
||||
|
Loading…
Reference in New Issue
Block a user