Merge branch 'main' into next

This commit is contained in:
Andras Bacsai 2022-12-13 09:35:33 +01:00
commit 1639d1725a
6 changed files with 300 additions and 206 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: '0.8.0' defaultVersion: "0.8.0"
documentation: https://pocketbase.io/docs/ documentation: https://pocketbase.io/docs/
type: pocketbase type: pocketbase
name: Pocketbase name: Pocketbase
@ -12,7 +12,7 @@
ports: ports:
- "8080" - "8080"
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 1.5.0-rc.0 defaultVersion: v1.5.1
documentation: https://plausible.io/doc/ documentation: https://plausible.io/doc/
type: plausibleanalytics-arm type: plausibleanalytics-arm
name: Plausible Analytics (ARM) name: Plausible Analytics (ARM)
@ -313,7 +313,7 @@
defaultValue: keycloak defaultValue: keycloak
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: v3.6 defaultVersion: v3.7
documentation: https://github.com/freyacodes/Lavalink documentation: https://github.com/freyacodes/Lavalink
description: Standalone audio sending node based on Lavaplayer. description: Standalone audio sending node based on Lavaplayer.
type: lavalink type: lavalink
@ -375,7 +375,7 @@
defaultValue: $$generate_password defaultValue: $$generate_password
required: true required: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: v1.8.6 defaultVersion: v1.8.9
documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/ documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/
type: appsmith type: appsmith
name: Appsmith name: Appsmith
@ -408,7 +408,7 @@
defaultValue: "true" defaultValue: "true"
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 0.56.2 defaultVersion: 0.57.4
documentation: https://hub.docker.com/r/zadam/trilium documentation: https://hub.docker.com/r/zadam/trilium
description: "A hierarchical note taking application with focus on building large personal knowledge bases." description: "A hierarchical note taking application with focus on building large personal knowledge bases."
labels: labels:
@ -428,7 +428,7 @@
- "8080" - "8080"
variables: [] variables: []
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 1.9.2 defaultVersion: 1.18.5
documentation: https://hub.docker.com/r/louislam/uptime-kuma documentation: https://hub.docker.com/r/louislam/uptime-kuma
description: A free & fancy self-hosted monitoring tool. description: A free & fancy self-hosted monitoring tool.
labels: labels:
@ -478,7 +478,7 @@
- "80" - "80"
variables: [] variables: []
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 9.2.3 defaultVersion: 9.3.1
documentation: https://hub.docker.com/r/grafana/grafana documentation: https://hub.docker.com/r/grafana/grafana
type: grafana type: grafana
name: Grafana name: Grafana
@ -499,7 +499,7 @@
- "3000" - "3000"
variables: [] variables: []
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 1.0.3 defaultVersion: 1.1.2
documentation: https://appwrite.io/docs documentation: https://appwrite.io/docs
type: appwrite type: appwrite
name: Appwrite name: Appwrite
@ -1669,7 +1669,7 @@
defaultValue: weblate defaultValue: weblate
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 2022.10.14-1a5b0965 defaultVersion: 2022.12.12-966e9c3c
documentation: https://docs.searxng.org/ documentation: https://docs.searxng.org/
type: searxng type: searxng
name: SearXNG name: SearXNG
@ -1742,7 +1742,7 @@
defaultValue: $$generate_password defaultValue: $$generate_password
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: v2.0.6 defaultVersion: v3.0.0
documentation: https://glitchtip.com/documentation documentation: https://glitchtip.com/documentation
type: glitchtip type: glitchtip
name: GlitchTip name: GlitchTip
@ -1964,7 +1964,7 @@
defaultValue: glitchtip defaultValue: glitchtip
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: v2.13.0 defaultVersion: v2.16.0
documentation: https://hasura.io/docs/latest/index/ documentation: https://hasura.io/docs/latest/index/
type: hasura type: hasura
name: Hasura name: Hasura
@ -2031,7 +2031,7 @@
defaultValue: hasura defaultValue: hasura
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: postgresql-v1.38.0 defaultVersion: postgresql-v1.39.5
documentation: https://umami.is/docs/getting-started documentation: https://umami.is/docs/getting-started
type: umami-postgresql type: umami-postgresql
name: Umami name: Umami
@ -2238,7 +2238,7 @@
showOnConfiguration: true showOnConfiguration: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
ignore: true ignore: true
defaultVersion: postgresql-v1.38.0 defaultVersion: postgresql-v1.39.5
documentation: https://umami.is/docs/getting-started documentation: https://umami.is/docs/getting-started
type: umami type: umami
name: Umami name: Umami
@ -2444,7 +2444,7 @@
description: "" description: ""
showOnConfiguration: true showOnConfiguration: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: v0.29.1 defaultVersion: v0.30.1
documentation: https://docs.meilisearch.com/learn/getting_started/quick_start.html documentation: https://docs.meilisearch.com/learn/getting_started/quick_start.html
type: meilisearch type: meilisearch
name: MeiliSearch name: MeiliSearch
@ -2592,7 +2592,7 @@
defaultValue: $$generate_password defaultValue: $$generate_password
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: "5.22" defaultVersion: "5.25.3"
documentation: https://docs.ghost.org documentation: https://docs.ghost.org
type: ghost-only type: ghost-only
name: Ghost name: Ghost
@ -2656,7 +2656,7 @@
placeholder: "ghost_db" placeholder: "ghost_db"
required: true required: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: "5.22" defaultVersion: "5.25.3"
documentation: https://docs.ghost.org documentation: https://docs.ghost.org
type: ghost-mysql type: ghost-mysql
name: Ghost name: Ghost
@ -2897,7 +2897,7 @@
define('WP_DEBUG_DISPLAY', false); define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', 0); @ini_set('display_errors', 0);
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 4.7.1 defaultVersion: 4.9.0
documentation: https://coder.com/docs/coder-oss/latest documentation: https://coder.com/docs/coder-oss/latest
type: vscodeserver type: vscodeserver
name: VSCode Server name: VSCode Server
@ -2928,7 +2928,7 @@
description: "" description: ""
showOnConfiguration: true showOnConfiguration: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: RELEASE.2022-10-15T19-57-03Z defaultVersion: RELEASE.2022-12-12T19-27-27Z
documentation: https://min.io/docs/minio documentation: https://min.io/docs/minio
type: minio type: minio
name: MinIO name: MinIO
@ -3106,7 +3106,7 @@
defaultValue: $$generate_username defaultValue: $$generate_username
description: "" description: ""
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 0.198.1 defaultVersion: 0.207.0
documentation: https://docs.n8n.io documentation: https://docs.n8n.io
type: n8n type: n8n
name: n8n.io name: n8n.io
@ -3283,7 +3283,7 @@
defaultValue: plausible.js defaultValue: plausible.js
description: This is the default script name. description: This is the default script name.
- templateVersion: 1.0.0 - templateVersion: 1.0.0
defaultVersion: 0.98.1 defaultVersion: 0.99.1
documentation: https://docs.nocodb.com documentation: https://docs.nocodb.com
type: nocodb type: nocodb
name: NocoDB name: NocoDB

View File

@ -19,9 +19,10 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common
import { scheduler } from './scheduler'; import { scheduler } from './scheduler';
import type { ExecaChildProcess } from 'execa'; import type { ExecaChildProcess } from 'execa';
export const version = '3.12.0'; export const version = '3.12.1';
export const isDev = process.env.NODE_ENV === 'development'; export const isDev = process.env.NODE_ENV === 'development';
export const sentryDSN = 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; export const sentryDSN =
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
const algorithm = 'aes-256-ctr'; const algorithm = 'aes-256-ctr';
const customConfig: Config = { const customConfig: Config = {
dictionaries: [adjectives, colors, animals], dictionaries: [adjectives, colors, animals],
@ -92,7 +93,7 @@ export const asyncExecShellStream = async ({
line: `${line.replace('\n', '')}`, line: `${line.replace('\n', '')}`,
buildId, buildId,
applicationId applicationId
} };
logs.push(log); logs.push(log);
if (debug) { if (debug) {
await saveBuildLog(log); await saveBuildLog(log);
@ -109,7 +110,7 @@ export const asyncExecShellStream = async ({
line: `${line.replace('\n', '')}`, line: `${line.replace('\n', '')}`,
buildId, buildId,
applicationId applicationId
} };
logs.push(log); logs.push(log);
if (debug) { if (debug) {
await saveBuildLog(log); await saveBuildLog(log);
@ -393,7 +394,6 @@ export function generateTimestamp(): string {
return `${day().format('HH:mm:ss.SSS')}`; return `${day().format('HH:mm:ss.SSS')}`;
} }
export const supportedDatabaseTypesAndVersions = [ export const supportedDatabaseTypesAndVersions = [
{ {
name: 'mongodb', name: 'mongodb',
@ -509,20 +509,19 @@ export async function createRemoteEngineConfiguration(id: string) {
} = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } }); } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } });
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 }); await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 });
const config = sshConfig.parse(''); const config = sshConfig.parse('');
const Host = `${remoteIpAddress}-remote` const Host = `${remoteIpAddress}-remote`;
try { try {
await executeCommand({ command: `ssh-keygen -R ${Host}` }); await executeCommand({ command: `ssh-keygen -R ${Host}` });
await executeCommand({ command: `ssh-keygen -R ${remoteIpAddress}` }); await executeCommand({ command: `ssh-keygen -R ${remoteIpAddress}` });
await executeCommand({ command: `ssh-keygen -R localhost:${localPort}` }); await executeCommand({ command: `ssh-keygen -R localhost:${localPort}` });
} catch (error) { } } catch (error) {}
const found = config.find({ Host }); const found = config.find({ Host });
const foundIp = config.find({ Host: remoteIpAddress }); const foundIp = config.find({ Host: remoteIpAddress });
if (found) config.remove({ Host }) if (found) config.remove({ Host });
if (foundIp) config.remove({ Host: remoteIpAddress }) if (foundIp) config.remove({ Host: remoteIpAddress });
config.append({ config.append({
Host, Host,
@ -543,15 +542,35 @@ export async function createRemoteEngineConfiguration(id: string) {
} }
return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config)); return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config));
} }
export async function executeCommand({ command, dockerId = null, sshCommand = false, shell = false, stream = false, buildId, applicationId, debug }: { command: string, sshCommand?: boolean, shell?: boolean, stream?: boolean, dockerId?: string, buildId?: string, applicationId?: string, debug?: boolean }): Promise<ExecaChildProcess<string>> { export async function executeCommand({
const { execa, execaCommand } = await import('execa') command,
const { parse } = await import('shell-quote') dockerId = null,
sshCommand = false,
shell = false,
stream = false,
buildId,
applicationId,
debug
}: {
command: string;
sshCommand?: boolean;
shell?: boolean;
stream?: boolean;
dockerId?: string;
buildId?: string;
applicationId?: string;
debug?: boolean;
}): Promise<ExecaChildProcess<string>> {
const { execa, execaCommand } = await import('execa');
const { parse } = await import('shell-quote');
const parsedCommand = parse(command); const parsedCommand = parse(command);
const dockerCommand = parsedCommand[0]; const dockerCommand = parsedCommand[0];
const dockerArgs = parsedCommand.slice(1); const dockerArgs = parsedCommand.slice(1);
if (dockerId) { if (dockerId) {
let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } }) let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({
where: { id: dockerId }
});
if (remoteEngine) { if (remoteEngine) {
await createRemoteEngineConfiguration(dockerId); await createRemoteEngineConfiguration(dockerId);
engine = `ssh://${remoteIpAddress}-remote`; engine = `ssh://${remoteIpAddress}-remote`;
@ -591,7 +610,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
line: `${line.replace('\n', '')}`, line: `${line.replace('\n', '')}`,
buildId, buildId,
applicationId applicationId
} };
logs.push(log); logs.push(log);
if (debug) { if (debug) {
await saveBuildLog(log); await saveBuildLog(log);
@ -608,7 +627,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
line: `${line.replace('\n', '')}`, line: `${line.replace('\n', '')}`,
buildId, buildId,
applicationId applicationId
} };
logs.push(log); logs.push(log);
if (debug) { if (debug) {
await saveBuildLog(log); await saveBuildLog(log);
@ -628,7 +647,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
reject(code); reject(code);
} }
}); });
}) });
} else { } else {
if (shell) { if (shell) {
return await execaCommand(command, { return await execaCommand(command, {
@ -640,7 +659,6 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
}); });
} }
} }
} else { } else {
if (shell) { if (shell) {
return execaCommand(command, { shell: true }); return execaCommand(command, { shell: true });
@ -650,8 +668,13 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
} }
export async function startTraefikProxy(id: string): Promise<void> { export async function startTraefikProxy(id: string): Promise<void> {
const { engine, network, remoteEngine, remoteIpAddress } = await prisma.destinationDocker.findUnique({ where: { id } }) const { engine, network, remoteEngine, remoteIpAddress } =
const { found } = await checkContainer({ dockerId: id, container: 'coolify-proxy', remove: true }); await prisma.destinationDocker.findUnique({ where: { id } });
const { found } = await checkContainer({
dockerId: id,
container: 'coolify-proxy',
remove: true
});
const { id: settingsId, ipv4, ipv6 } = await listSettings(); const { id: settingsId, ipv4, ipv6 } = await listSettings();
if (!found) { if (!found) {
@ -768,9 +791,12 @@ export async function listSettings(): Promise<any> {
} }
export function generateToken() { export function generateToken() {
return jsonwebtoken.sign({ return jsonwebtoken.sign(
nbf: Math.floor(Date.now() / 1000) - 30, {
}, process.env['COOLIFY_SECRET_KEY']) nbf: Math.floor(Date.now() / 1000) - 30
},
process.env['COOLIFY_SECRET_KEY']
);
} }
export function generatePassword({ export function generatePassword({
length = 24, length = 24,
@ -790,109 +816,102 @@ export function generatePassword({
return password; return password;
} }
type DatabaseConfiguration = { type DatabaseConfiguration =
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
MYSQL_DATABASE: string;
MYSQL_PASSWORD: string;
MYSQL_ROOT_USER: string;
MYSQL_USER: string;
MYSQL_ROOT_PASSWORD: string;
};
}
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
MONGO_INITDB_ROOT_USERNAME?: string; MYSQL_DATABASE: string;
MONGO_INITDB_ROOT_PASSWORD?: string; MYSQL_PASSWORD: string;
MONGODB_ROOT_USER?: string; MYSQL_ROOT_USER: string;
MONGODB_ROOT_PASSWORD?: string; MYSQL_USER: string;
}; MYSQL_ROOT_PASSWORD: string;
} };
}
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
MARIADB_ROOT_USER: string; MONGO_INITDB_ROOT_USERNAME?: string;
MARIADB_ROOT_PASSWORD: string; MONGO_INITDB_ROOT_PASSWORD?: string;
MARIADB_USER: string; MONGODB_ROOT_USER?: string;
MARIADB_PASSWORD: string; MONGODB_ROOT_PASSWORD?: string;
MARIADB_DATABASE: string; };
}; }
}
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
POSTGRES_PASSWORD?: string; MARIADB_ROOT_USER: string;
POSTGRES_USER?: string; MARIADB_ROOT_PASSWORD: string;
POSTGRES_DB?: string; MARIADB_USER: string;
POSTGRESQL_POSTGRES_PASSWORD?: string; MARIADB_PASSWORD: string;
POSTGRESQL_USERNAME?: string; MARIADB_DATABASE: string;
POSTGRESQL_PASSWORD?: string; };
POSTGRESQL_DATABASE?: string; }
};
}
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
REDIS_AOF_ENABLED: string; POSTGRES_PASSWORD?: string;
REDIS_PASSWORD: string; POSTGRES_USER?: string;
}; POSTGRES_DB?: string;
} POSTGRESQL_POSTGRES_PASSWORD?: string;
POSTGRESQL_USERNAME?: string;
POSTGRESQL_PASSWORD?: string;
POSTGRESQL_DATABASE?: string;
};
}
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
COUCHDB_PASSWORD: string; REDIS_AOF_ENABLED: string;
COUCHDB_USER: string; REDIS_PASSWORD: string;
}; };
} }
| { | {
volume: string; volume: string;
image: string; image: string;
command?: string; command?: string;
ulimits: Record<string, unknown>; ulimits: Record<string, unknown>;
privatePort: number; privatePort: number;
environmentVariables: { environmentVariables: {
EDGEDB_SERVER_PASSWORD: string; COUCHDB_PASSWORD: string;
EDGEDB_SERVER_USER: string; COUCHDB_USER: string;
EDGEDB_SERVER_DATABASE: string; };
EDGEDB_SERVER_TLS_CERT_MODE: string; }
}; | {
} volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
EDGEDB_SERVER_PASSWORD: string;
EDGEDB_SERVER_USER: string;
EDGEDB_SERVER_DATABASE: string;
EDGEDB_SERVER_TLS_CERT_MODE: string;
};
};
export function generateDatabaseConfiguration(database: any, arch: string): DatabaseConfiguration { export function generateDatabaseConfiguration(database: any, arch: string): DatabaseConfiguration {
const { const { id, dbUser, dbUserPassword, rootUser, rootUserPassword, defaultDatabase, version, type } =
id, database;
dbUser,
dbUserPassword,
rootUser,
rootUserPassword,
defaultDatabase,
version,
type,
} = database;
const baseImage = getDatabaseImage(type, arch); const baseImage = getDatabaseImage(type, arch);
if (type === 'mysql') { if (type === 'mysql') {
const configuration = { const configuration = {
@ -972,7 +991,9 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
} }
return configuration; return configuration;
} else if (type === 'redis') { } else if (type === 'redis') {
const { settings: { appendOnly } } = database; const {
settings: { appendOnly }
} = database;
const configuration: DatabaseConfiguration = { const configuration: DatabaseConfiguration = {
privatePort: 6379, privatePort: 6379,
command: undefined, command: undefined,
@ -986,8 +1007,9 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
}; };
if (isARM(arch)) { if (isARM(arch)) {
configuration.volume = `${id}-${type}-data:/data`; configuration.volume = `${id}-${type}-data:/data`;
configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no' configuration.command = `/usr/local/bin/redis-server --appendonly ${
} --requirepass ${dbUserPassword}`; appendOnly ? 'yes' : 'no'
} --requirepass ${dbUserPassword}`;
} }
return configuration; return configuration;
} else if (type === 'couchdb') { } else if (type === 'couchdb') {
@ -1004,7 +1026,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
if (isARM(arch)) { if (isARM(arch)) {
configuration.volume = `${id}-${type}-data:/opt/couchdb/data`; configuration.volume = `${id}-${type}-data:/opt/couchdb/data`;
} }
return configuration return configuration;
} else if (type === 'edgedb') { } else if (type === 'edgedb') {
const configuration: DatabaseConfiguration = { const configuration: DatabaseConfiguration = {
privatePort: 5656, privatePort: 5656,
@ -1018,7 +1040,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
volume: `${id}-${type}-data:/var/lib/edgedb/data`, volume: `${id}-${type}-data:/var/lib/edgedb/data`,
ulimits: {} ulimits: {}
}; };
return configuration return configuration;
} }
} }
export function isARM(arch: string) { export function isARM(arch: string) {
@ -1071,12 +1093,12 @@ export type ComposeFileService = {
command?: string; command?: string;
ports?: string[]; ports?: string[];
build?: build?:
| { | {
context: string; context: string;
dockerfile: string; dockerfile: string;
args?: Record<string, unknown>; args?: Record<string, unknown>;
} }
| string; | string;
deploy?: { deploy?: {
restart_policy?: { restart_policy?: {
condition?: string; condition?: string;
@ -1141,13 +1163,13 @@ export const createDirectories = async ({
repository: string; repository: string;
buildId: string; buildId: string;
}): Promise<{ workdir: string; repodir: string }> => { }): Promise<{ workdir: string; repodir: string }> => {
if (repository) repository = repository.replaceAll(' ', '') if (repository) repository = repository.replaceAll(' ', '');
const repodir = `/tmp/build-sources/${repository}/`; const repodir = `/tmp/build-sources/${repository}/`;
const workdir = `/tmp/build-sources/${repository}/${buildId}`; const workdir = `/tmp/build-sources/${repository}/${buildId}`;
let workdirFound = false; let workdirFound = false;
try { try {
workdirFound = !!(await fs.stat(workdir)); workdirFound = !!(await fs.stat(workdir));
} catch (error) { } } catch (error) {}
if (workdirFound) { if (workdirFound) {
await executeCommand({ command: `rm -fr ${workdir}` }); await executeCommand({ command: `rm -fr ${workdir}` });
} }
@ -1254,19 +1276,45 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
} }
} }
} }
export async function checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress }: { id: string, configuredPort?: number, exposePort: number, engine: string, remoteEngine: boolean, remoteIpAddress?: string }) { export async function checkExposedPort({
id,
configuredPort,
exposePort,
engine,
remoteEngine,
remoteIpAddress
}: {
id: string;
configuredPort?: number;
exposePort: number;
engine: string;
remoteEngine: boolean;
remoteIpAddress?: string;
}) {
if (exposePort < 1024 || exposePort > 65535) { if (exposePort < 1024 || exposePort > 65535) {
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }; throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` };
} }
if (configuredPort) { if (configuredPort) {
if (configuredPort !== exposePort) { if (configuredPort !== exposePort) {
const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress); const availablePort = await getFreeExposedPort(
id,
exposePort,
engine,
remoteEngine,
remoteIpAddress
);
if (availablePort.toString() !== exposePort.toString()) { if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }; throw { status: 500, message: `Port ${exposePort} is already in use.` };
} }
} }
} else { } else {
const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress); const availablePort = await getFreeExposedPort(
id,
exposePort,
engine,
remoteEngine,
remoteIpAddress
);
if (availablePort.toString() !== exposePort.toString()) { if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }; throw { status: 500, message: `Port ${exposePort} is already in use.` };
} }
@ -1277,25 +1325,33 @@ export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, r
if (remoteEngine) { if (remoteEngine) {
const applicationUsed = await ( const applicationUsed = await (
await prisma.application.findMany({ await prisma.application.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } }, where: {
exposePort: { not: null },
id: { not: id },
destinationDocker: { remoteIpAddress }
},
select: { exposePort: true } select: { exposePort: true }
}) })
).map((a) => a.exposePort); ).map((a) => a.exposePort);
const serviceUsed = await ( const serviceUsed = await (
await prisma.service.findMany({ await prisma.service.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } }, where: {
exposePort: { not: null },
id: { not: id },
destinationDocker: { remoteIpAddress }
},
select: { exposePort: true } select: { exposePort: true }
}) })
).map((a) => a.exposePort); ).map((a) => a.exposePort);
const usedPorts = [...applicationUsed, ...serviceUsed]; const usedPorts = [...applicationUsed, ...serviceUsed];
if (usedPorts.includes(exposePort)) { if (usedPorts.includes(exposePort)) {
return false return false;
} }
const found = await checkPort(exposePort, { host: remoteIpAddress }); const found = await checkPort(exposePort, { host: remoteIpAddress });
if (!found) { if (!found) {
return exposePort return exposePort;
} }
return false return false;
} else { } else {
const applicationUsed = await ( const applicationUsed = await (
await prisma.application.findMany({ await prisma.application.findMany({
@ -1311,13 +1367,13 @@ export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, r
).map((a) => a.exposePort); ).map((a) => a.exposePort);
const usedPorts = [...applicationUsed, ...serviceUsed]; const usedPorts = [...applicationUsed, ...serviceUsed];
if (usedPorts.includes(exposePort)) { if (usedPorts.includes(exposePort)) {
return false return false;
} }
const found = await checkPort(exposePort, { host: 'localhost' }); const found = await checkPort(exposePort, { host: 'localhost' });
if (!found) { if (!found) {
return exposePort return exposePort;
} }
return false return false;
} }
} }
export function generateRangeArray(start, end) { export function generateRangeArray(start, end) {
@ -1330,38 +1386,54 @@ export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddr
if (remoteEngine) { if (remoteEngine) {
const dbUsed = await ( const dbUsed = await (
await prisma.database.findMany({ await prisma.database.findMany({
where: { publicPort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } }, where: {
publicPort: { not: null },
id: { not: id },
destinationDocker: { remoteIpAddress }
},
select: { publicPort: true } select: { publicPort: true }
}) })
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const wpFtpUsed = await ( const wpFtpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } }, where: {
ftpPublicPort: { not: null },
id: { not: id },
service: { destinationDocker: { remoteIpAddress } }
},
select: { ftpPublicPort: true } select: { ftpPublicPort: true }
}) })
).map((a) => a.ftpPublicPort); ).map((a) => a.ftpPublicPort);
const wpUsed = await ( const wpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } }, where: {
mysqlPublicPort: { not: null },
id: { not: id },
service: { destinationDocker: { remoteIpAddress } }
},
select: { mysqlPublicPort: true } select: { mysqlPublicPort: true }
}) })
).map((a) => a.mysqlPublicPort); ).map((a) => a.mysqlPublicPort);
const minioUsed = await ( const minioUsed = await (
await prisma.minio.findMany({ await prisma.minio.findMany({
where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } }, where: {
publicPort: { not: null },
id: { not: id },
service: { destinationDocker: { remoteIpAddress } }
},
select: { publicPort: true } select: { publicPort: true }
}) })
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed]; const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
const range = generateRangeArray(minPort, maxPort) const range = generateRangeArray(minPort, maxPort);
const availablePorts = range.filter(port => !usedPorts.includes(port)) const availablePorts = range.filter((port) => !usedPorts.includes(port));
for (const port of availablePorts) { for (const port of availablePorts) {
const found = await isReachable(port, { host: remoteIpAddress }) const found = await isReachable(port, { host: remoteIpAddress });
if (!found) { if (!found) {
return port return port;
} }
} }
return false return false;
} else { } else {
const dbUsed = await ( const dbUsed = await (
await prisma.database.findMany({ await prisma.database.findMany({
@ -1371,32 +1443,44 @@ export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddr
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const wpFtpUsed = await ( const wpFtpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } }, where: {
ftpPublicPort: { not: null },
id: { not: id },
service: { destinationDocker: { engine } }
},
select: { ftpPublicPort: true } select: { ftpPublicPort: true }
}) })
).map((a) => a.ftpPublicPort); ).map((a) => a.ftpPublicPort);
const wpUsed = await ( const wpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } }, where: {
mysqlPublicPort: { not: null },
id: { not: id },
service: { destinationDocker: { engine } }
},
select: { mysqlPublicPort: true } select: { mysqlPublicPort: true }
}) })
).map((a) => a.mysqlPublicPort); ).map((a) => a.mysqlPublicPort);
const minioUsed = await ( const minioUsed = await (
await prisma.minio.findMany({ await prisma.minio.findMany({
where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } }, where: {
publicPort: { not: null },
id: { not: id },
service: { destinationDocker: { engine } }
},
select: { publicPort: true } select: { publicPort: true }
}) })
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed]; const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
const range = generateRangeArray(minPort, maxPort) const range = generateRangeArray(minPort, maxPort);
const availablePorts = range.filter(port => !usedPorts.includes(port)) const availablePorts = range.filter((port) => !usedPorts.includes(port));
for (const port of availablePorts) { for (const port of availablePorts) {
const found = await isReachable(port, { host: 'localhost' }) const found = await isReachable(port, { host: 'localhost' });
if (!found) { if (!found) {
return port return port;
} }
} }
return false return false;
} }
} }
@ -1502,11 +1586,11 @@ export async function getServiceFromDB({
serviceSecret: true, serviceSecret: true,
serviceSetting: true, serviceSetting: true,
wordpress: true, wordpress: true,
plausibleAnalytics: true, plausibleAnalytics: true
} }
}); });
if (!body) { if (!body) {
return null return null;
} }
// body.type = fixType(body.type); // body.type = fixType(body.type);
@ -1523,7 +1607,6 @@ export async function getServiceFromDB({
return { ...body, settings }; return { ...body, settings };
} }
export function fixType(type) { export function fixType(type) {
return type?.replaceAll(' ', '').toLowerCase() || null; return type?.replaceAll(' ', '').toLowerCase() || null;
} }
@ -1610,7 +1693,7 @@ export async function stopBuild(buildId, applicationId) {
} }
} }
count++; count++;
} catch (error) { } } catch (error) {}
}, 100); }, 100);
}); });
} }
@ -1640,16 +1723,22 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
images = images.trim(); images = images.trim();
if (images) { if (images) {
await executeCommand({ dockerId, command: `docker rmi -f ${images}" -q | xargs -r`, shell: true }); await executeCommand({
dockerId,
command: `docker rmi -f ${images}" -q | xargs -r`,
shell: true
});
} }
} catch (error) { } } catch (error) {}
if (lowDiskSpace || force) { if (lowDiskSpace || force) {
// Cleanup images that are not used // Cleanup images that are not used
try { try {
await executeCommand({ dockerId, command: `docker image prune -f` }); await executeCommand({ dockerId, command: `docker image prune -f` });
} catch (error) { } } catch (error) {}
const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({ where: { id: '0' } }) const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({
where: { id: '0' }
});
const { stdout: images } = await executeCommand({ const { stdout: images } = await executeCommand({
dockerId, dockerId,
command: `docker images|grep -v "<none>"|grep -v REPOSITORY|awk '{print $1, $2}'`, command: `docker images|grep -v "<none>"|grep -v REPOSITORY|awk '{print $1, $2}'`,
@ -1657,18 +1746,17 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
}); });
const imagesArray = images.trim().replaceAll(' ', ':').split('\n'); const imagesArray = images.trim().replaceAll(' ', ':').split('\n');
const imagesSet = new Set(imagesArray.map((image) => image.split(':')[0])); const imagesSet = new Set(imagesArray.map((image) => image.split(':')[0]));
let deleteImage = [] let deleteImage = [];
for (const image of imagesSet) { for (const image of imagesSet) {
let keepImage = [] let keepImage = [];
for (const image2 of imagesArray) { for (const image2 of imagesArray) {
if (image2.startsWith(image)) { if (image2.startsWith(image)) {
if (keepImage.length >= numberOfDockerImagesKeptLocally) { if (keepImage.length >= numberOfDockerImagesKeptLocally) {
deleteImage.push(image2) deleteImage.push(image2);
} else { } else {
keepImage.push(image2) keepImage.push(image2);
} }
} }
} }
} }
for (const image of deleteImage) { for (const image of deleteImage) {
@ -1681,12 +1769,12 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
dockerId, dockerId,
command: `docker container prune -f --filter "label=coolify.managed=true"` command: `docker container prune -f --filter "label=coolify.managed=true"`
}); });
} catch (error) { } } catch (error) {}
// Cleanup build caches // Cleanup build caches
try { try {
await executeCommand({ dockerId, command: `docker builder prune -a -f` }); await executeCommand({ dockerId, command: `docker builder prune -a -f` });
} catch (error) { } } catch (error) {}
} }
} }
@ -1768,16 +1856,22 @@ export function decryptApplication(application: any) {
} }
} }
export async function pushToRegistry(application: any, workdir: string, tag: string, imageName: string, customTag: string) { export async function pushToRegistry(
const location = `${workdir}/.docker` application: any,
const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}` workdir: string,
const pushCommand = `docker --config ${location} push ${imageName}:${customTag}` tag: string,
imageName: string,
customTag: string
) {
const location = `${workdir}/.docker`;
const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}`;
const pushCommand = `docker --config ${location} push ${imageName}:${customTag}`;
await executeCommand({ await executeCommand({
dockerId: application.destinationDockerId, dockerId: application.destinationDockerId,
command: tagCommand command: tagCommand
}) });
await executeCommand({ await executeCommand({
dockerId: application.destinationDockerId, dockerId: application.destinationDockerId,
command: pushCommand command: pushCommand
}) });
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.12.0", "version": "3.12.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": "github:coollabsio/coolify", "repository": "github:coollabsio/coolify",
"scripts": { "scripts": {