Merge branch 'next' into feature/glitchtip-service

This commit is contained in:
Andras Bacsai
2022-08-18 21:47:06 +02:00
committed by GitHub
39 changed files with 1031 additions and 612 deletions

View File

@@ -5,8 +5,10 @@ import env from '@fastify/env';
import cookie from '@fastify/cookie';
import path, { join } from 'path';
import autoLoad from '@fastify/autoload';
import { asyncExecShell, isDev, listSettings, prisma } from './lib/common';
import { asyncExecShell, isDev, listSettings, prisma, version } from './lib/common';
import { scheduler } from './lib/scheduler';
import axios from 'axios';
import compareVersions from 'compare-versions';
declare module 'fastify' {
interface FastifyInstance {
@@ -113,8 +115,22 @@ fastify.listen({ port, host }, async (err: any, address: any) => {
setInterval(async () => {
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
if (isAutoUpdateEnabled) {
if (scheduler.workers.has('deployApplication')) {
scheduler.workers.get('deployApplication').postMessage("status:autoUpdater");
const currentVersion = version;
const { data: versions } = await axios
.get(
`https://get.coollabs.io/versions.json`
, {
params: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
})
const latestVersion = versions['coolify'].main.version;
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
if (isUpdateAvailable === 1) {
if (scheduler.workers.has('deployApplication')) {
scheduler.workers.get('deployApplication').postMessage("status:autoUpdater");
}
}
}
}, isDev ? 5000 : 60000 * 15)

View File

@@ -4,7 +4,7 @@ import fs from 'fs/promises';
import yaml from 'js-yaml';
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { createDirectories, decrypt, executeDockerCmd, getDomain, prisma } from '../lib/common';
import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma } from '../lib/common';
import * as importers from '../lib/importers';
import * as buildpacks from '../lib/buildPacks';
@@ -56,6 +56,7 @@ import * as buildpacks from '../lib/buildPacks';
baseImage,
baseBuildImage,
deploymentType,
forceRebuild
} = message
let {
branch,
@@ -69,6 +70,30 @@ import * as buildpacks from '../lib/buildPacks';
dockerFileLocation,
denoMainFile
} = message
const currentHash = crypto
.createHash('sha256')
.update(
JSON.stringify({
pythonWSGI,
pythonModule,
pythonVariable,
deploymentType,
denoOptions,
baseImage,
baseBuildImage,
buildPack,
port,
exposePort,
installCommand,
buildCommand,
startCommand,
secrets,
branch,
repository,
fqdn
})
)
.digest('hex');
try {
const { debug } = settings;
if (concurrency === 1) {
@@ -131,7 +156,8 @@ import * as buildpacks from '../lib/buildPacks';
htmlUrl: gitSource.htmlUrl,
projectId,
deployKeyId: gitSource.gitlabApp?.deployKeyId || null,
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null,
forPublic: gitSource.forPublic
});
if (!commit) {
throw new Error('No commit found?');
@@ -146,38 +172,10 @@ import * as buildpacks from '../lib/buildPacks';
} catch (err) {
console.log(err);
}
if (!pullmergeRequestId) {
const currentHash = crypto
//@ts-ignore
.createHash('sha256')
.update(
JSON.stringify({
pythonWSGI,
pythonModule,
pythonVariable,
deploymentType,
denoOptions,
baseImage,
baseBuildImage,
buildPack,
port,
exposePort,
installCommand,
buildCommand,
startCommand,
secrets,
branch,
repository,
fqdn
})
)
.digest('hex');
if (configHash !== currentHash) {
await prisma.application.update({
where: { id: applicationId },
data: { configHash: currentHash }
});
deployNeeded = true;
if (configHash) {
await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
@@ -200,8 +198,10 @@ import * as buildpacks from '../lib/buildPacks';
//
}
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
if (forceRebuild) deployNeeded = true
if (!imageFound || deployNeeded) {
// if (true) {
// if (true) {
if (buildpacks[buildPack])
await buildpacks[buildPack]({
dockerId: destinationDocker.id,
@@ -250,16 +250,18 @@ import * as buildpacks from '../lib/buildPacks';
} catch (error) {
//
}
const envs = [];
const envs = [
`PORT=${port}`
];
if (secrets.length > 0) {
secrets.forEach((secret) => {
if (pullmergeRequestId) {
if (secret.isPRMRSecret) {
envs.push(`${secret.name}='${secret.value}'`);
envs.push(`${secret.name}=${secret.value}`);
}
} else {
if (!secret.isPRMRSecret) {
envs.push(`${secret.name}='${secret.value}'`);
envs.push(`${secret.name}=${secret.value}`);
}
}
});
@@ -306,23 +308,14 @@ import * as buildpacks from '../lib/buildPacks';
container_name: imageId,
volumes,
env_file: envFound ? [`${workdir}/.env`] : [],
networks: [destinationDocker.network],
labels,
depends_on: [],
restart: 'always',
expose: [port],
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
// logging: {
// driver: 'fluentd',
// },
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
}
...defaultComposeConfiguration(destinationDocker.network),
}
},
networks: {
@@ -345,6 +338,10 @@ import * as buildpacks from '../lib/buildPacks';
}
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
await prisma.build.update({ where: { id: message.build_id }, data: { status: 'success' } });
if (!pullmergeRequestId) await prisma.application.update({
where: { id: applicationId },
data: { configHash: currentHash }
});
}
}

View File

@@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs';
import * as serviceFields from './serviceFields'
export const version = '3.5.0';
export const version = '3.6.0';
export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr';
@@ -319,6 +319,10 @@ export async function checkDoubleBranch(branch: string, projectId: number): Prom
}
export async function isDNSValid(hostname: any, domain: string): Promise<any> {
const { isIP } = await import('is-ip');
const { DNSServers } = await listSettings();
if (DNSServers) {
dns.setServers([DNSServers]);
}
let resolves = [];
try {
if (isIP(hostname)) {
@@ -332,7 +336,6 @@ export async function isDNSValid(hostname: any, domain: string): Promise<any> {
try {
let ipDomainFound = false;
dns.setServers(['1.1.1.1', '8.8.8.8']);
const dnsResolve = await dns.resolve4(domain);
if (dnsResolve.length > 0) {
for (const ip of dnsResolve) {
@@ -424,7 +427,12 @@ export async function checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }): P
const { isIP } = await import('is-ip');
const domain = getDomain(fqdn);
const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`;
dns.setServers(['1.1.1.1', '8.8.8.8']);
const { DNSServers } = await listSettings();
if (DNSServers) {
dns.setServers([DNSServers]);
}
let resolves = [];
try {
if (isIP(hostname)) {
@@ -1180,6 +1188,25 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
}
}
}
export async function checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress }: { id: string, configuredPort?: number, exposePort: number, dockerId: string, remoteIpAddress?: string }) {
if (exposePort < 1024 || exposePort > 65535) {
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
}
if (configuredPort) {
if (configuredPort !== exposePort) {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }
}
}
} else {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }
}
}
}
export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress) {
const { default: getPort } = await import('get-port');
const applicationUsed = await (
@@ -1565,7 +1592,7 @@ export async function configureServiceType({
});
} else if (type === 'appwrite') {
const opensslKeyV1 = encrypt(generatePassword());
const executorSecret = encrypt(generatePassword());
const executorSecret = encrypt(generatePassword());
const redisPassword = encrypt(generatePassword());
const mariadbHost = `${id}-mariadb`
const mariadbUser = cuid();
@@ -1845,3 +1872,17 @@ export function persistentVolumes(id, persistentStorage, config) {
) || {}
return { volumes, volumeMounts }
}
export function defaultComposeConfiguration(network: string): any {
return {
networks: [network],
restart: 'on-failure',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 10,
window: '120s'
}
}
}
}

View File

@@ -71,7 +71,6 @@ export async function removeContainer({
}): Promise<void> {
try {
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
console.log(id)
if (JSON.parse(stdout).Running) {
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })

View File

@@ -12,7 +12,8 @@ export default async function ({
htmlUrl,
branch,
buildId,
customPort
customPort,
forPublic
}: {
applicationId: string;
workdir: string;
@@ -23,41 +24,55 @@ export default async function ({
branch: string;
buildId: string;
customPort: number;
forPublic?: boolean;
}): Promise<string> {
const { default: got } = await import('got')
const url = htmlUrl.replace('https://', '').replace('http://', '');
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
if (forPublic) {
await saveBuildLog({
line: `Cloning ${repository}:${branch} branch.`,
buildId,
applicationId
});
await asyncExecShell(
`git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
);
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
if (body.privateKey) body.privateKey = decrypt(body.privateKey);
const { privateKey, appId, installationId } = body
} else {
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
if (body.privateKey) body.privateKey = decrypt(body.privateKey);
const { privateKey, appId, installationId } = body
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
const payload = {
iat: Math.round(new Date().getTime() / 1000),
exp: Math.round(new Date().getTime() / 1000 + 60),
iss: appId
};
const jwtToken = jsonwebtoken.sign(payload, githubPrivateKey, {
algorithm: 'RS256'
});
const { token } = await got
.post(`${apiUrl}/app/installations/${installationId}/access_tokens`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
Accept: 'application/vnd.github.machine-man-preview+json'
}
})
.json();
await saveBuildLog({
line: `Cloning ${repository}:${branch} branch.`,
buildId,
applicationId
});
await asyncExecShell(
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
);
const payload = {
iat: Math.round(new Date().getTime() / 1000),
exp: Math.round(new Date().getTime() / 1000 + 60),
iss: appId
};
const jwtToken = jsonwebtoken.sign(payload, githubPrivateKey, {
algorithm: 'RS256'
});
const { token } = await got
.post(`${apiUrl}/app/installations/${installationId}/access_tokens`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
Accept: 'application/vnd.github.machine-man-preview+json'
}
})
.json();
await saveBuildLog({
line: `Cloning ${repository}:${branch} branch.`,
buildId,
applicationId
});
await asyncExecShell(
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
);
}
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
return commit.replace('\n', '');
}

View File

@@ -20,7 +20,6 @@ const options: any = {
}
if (message.caller === 'cleanupStorage') {
if (!scheduler.workers.has('cleanupStorage')) {
await scheduler.stop('deployApplication');
await scheduler.run('cleanupStorage')
}
}

View File

@@ -17,19 +17,4 @@ export async function defaultServiceConfigurations({ id, teamId }) {
});
}
return { ...service, network, port, workdir, image, secrets }
}
export function defaultServiceComposeConfiguration(network: string): any {
return {
networks: [network],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '10s',
max_attempts: 10,
window: '120s'
}
}
}
}

View File

@@ -5,7 +5,7 @@ import axios from 'axios';
import { FastifyReply } from 'fastify';
import { day } from '../../../../lib/dayjs';
import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common';
import { checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, listSettings, prisma, stopBuild, uniqueName } from '../../../../lib/common';
import { checkDomainsIsValidInDNS, checkDoubleBranch, checkExposedPort, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, listSettings, prisma, stopBuild, uniqueName } from '../../../../lib/common';
import { checkContainer, formatLabelsOnDocker, isContainerExited, removeContainer } from '../../../../lib/docker';
import { scheduler } from '../../../../lib/scheduler';
@@ -18,7 +18,7 @@ export async function listApplications(request: FastifyRequest) {
const { teamId } = request.user
const applications = await prisma.application.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { teams: true, destinationDocker: true }
include: { teams: true, destinationDocker: true, settings: true }
});
const settings = await prisma.setting.findFirst()
return {
@@ -238,6 +238,9 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
if (exposePort) {
exposePort = Number(exposePort);
}
const { destinationDockerId } = await prisma.application.findUnique({ where: { id } })
if (exposePort) await checkExposedPort({ id, exposePort, dockerId: destinationDockerId })
if (denoOptions) denoOptions = denoOptions.trim();
const defaultConfiguration = await setDefaultConfiguration({
buildPack,
@@ -392,18 +395,7 @@ export async function checkDNS(request: FastifyRequest<CheckDNS>) {
if (found) {
throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` }
}
if (exposePort) {
if (exposePort < 1024 || exposePort > 65535) {
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
}
if (configuredPort !== exposePort) {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }
}
}
}
await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress })
if (isDNSCheckEnabled && !isDev && !forceSave) {
let hostname = request.hostname.split(':')[0];
if (remoteEngine) hostname = remoteIpAddress;
@@ -436,7 +428,7 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
try {
const { id } = request.params
const teamId = request.user?.teamId;
const { pullmergeRequestId = null, branch } = request.body
const { pullmergeRequestId = null, branch, forceRebuild } = request.body
const buildId = cuid();
const application = await getApplicationFromDB(id, teamId);
if (application) {
@@ -475,13 +467,15 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
type: 'manual',
...application,
sourceBranch: branch,
pullmergeRequestId
pullmergeRequestId,
forceRebuild
});
} else {
scheduler.workers.get('deployApplication').postMessage({
build_id: buildId,
type: 'manual',
...application
...application,
forceRebuild
});
}
@@ -499,11 +493,20 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
try {
const { id } = request.params
const { gitSourceId } = request.body
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
});
const { gitSourceId, forPublic, type } = request.body
if (forPublic) {
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: publicGit.id } } }
});
} else {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
});
}
return reply.code(201).send()
} catch ({ status, message }) {
return errorHandler({ status, message })
@@ -557,7 +560,7 @@ export async function checkRepository(request: FastifyRequest<CheckRepository>)
export async function saveRepository(request, reply) {
try {
const { id } = request.params
let { repository, branch, projectId, autodeploy, webhookToken } = request.body
let { repository, branch, projectId, autodeploy, webhookToken, isPublicRepository = false } = request.body
repository = repository.toLowerCase();
branch = branch.toLowerCase();
@@ -565,17 +568,19 @@ export async function saveRepository(request, reply) {
if (webhookToken) {
await prisma.application.update({
where: { id },
data: { repository, branch, projectId, gitSource: { update: { gitlabApp: { update: { webhookToken: webhookToken ? webhookToken : undefined } } } }, settings: { update: { autodeploy } } }
data: { repository, branch, projectId, gitSource: { update: { gitlabApp: { update: { webhookToken: webhookToken ? webhookToken : undefined } } } }, settings: { update: { autodeploy, isPublicRepository } } }
});
} else {
await prisma.application.update({
where: { id },
data: { repository, branch, projectId, settings: { update: { autodeploy } } }
data: { repository, branch, projectId, settings: { update: { autodeploy, isPublicRepository } } }
});
}
const isDouble = await checkDoubleBranch(branch, projectId);
if (isDouble) {
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
if (!isPublicRepository) {
const isDouble = await checkDoubleBranch(branch, projectId);
if (isDouble) {
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false, isPublicRepository } })
}
}
return reply.code(201).send()
} catch ({ status, message }) {
@@ -607,7 +612,8 @@ export async function getBuildPack(request) {
projectId: application.projectId,
repository: application.repository,
branch: application.branch,
apiUrl: application.gitSource.apiUrl
apiUrl: application.gitSource.apiUrl,
isPublicRepository: application.settings.isPublicRepository
}
} catch ({ status, message }) {
return errorHandler({ status, message })
@@ -657,13 +663,13 @@ export async function saveSecret(request: FastifyRequest<SaveSecret>, reply: Fas
if (found) {
throw { status: 500, message: `Secret ${name} already exists.` }
} else {
value = encrypt(value);
value = encrypt(value.trim());
await prisma.secret.create({
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
});
}
} else {
value = encrypt(value);
value = encrypt(value.trim());
const found = await prisma.secret.findFirst({ where: { applicationId: id, name, isPRMRSecret } });
if (found) {

View File

@@ -44,13 +44,13 @@ export interface CheckDNS extends OnlyId {
}
export interface DeployApplication {
Querystring: { domain: string }
Body: { pullmergeRequestId: string | null, branch: string }
Body: { pullmergeRequestId: string | null, branch: string, forceRebuild?: boolean }
}
export interface GetImages {
Body: { buildPack: string, deploymentType: string }
}
export interface SaveApplicationSource extends OnlyId {
Body: { gitSourceId: string }
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string }
}
export interface CheckRepository extends OnlyId {
Querystring: { repository: string, branch: string }
@@ -115,7 +115,8 @@ export interface CancelDeployment {
export interface DeployApplication extends OnlyId {
Body: {
pullmergeRequestId: string | null,
branch: string
branch: string,
forceRebuild?: boolean
}
}

View File

@@ -79,7 +79,6 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
if (id === 'new') {
console.log(engine)
if (engine) {
const { stdout } = await asyncExecShell(`DOCKER_HOST=unix:///var/run/docker.sock docker network ls --filter 'name=^${network}$' --format '{{json .}}'`);
if (stdout === '') {

View File

@@ -4,7 +4,7 @@ import axios from 'axios';
import compare from 'compare-versions';
import cuid from 'cuid';
import bcrypt from 'bcryptjs';
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, prisma, uniqueName, version } from '../../../lib/common';
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, listSettings, prisma, uniqueName, version } from '../../../lib/common';
import type { FastifyReply, FastifyRequest } from 'fastify';
import type { Login, Update } from '.';
@@ -97,7 +97,8 @@ export async function showDashboard(request: FastifyRequest) {
const userId = request.user.userId;
const teamId = request.user.teamId;
const applications = await prisma.application.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true }
});
const databases = await prisma.database.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
@@ -105,10 +106,12 @@ export async function showDashboard(request: FastifyRequest) {
const services = await prisma.service.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
});
const settings = await listSettings();
return {
applications,
databases,
services,
settings,
};
} catch ({ status, message }) {
return errorHandler({ status, message })

View File

@@ -2,14 +2,14 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import bcrypt from 'bcryptjs';
import { prisma, uniqueName, asyncExecShell, getServiceImage, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, supportedServiceTypesAndVersions, executeDockerCmd, listSettings, getFreeExposedPort, checkDomainsIsValidInDNS, persistentVolumes, asyncSleep, isARM } from '../../../../lib/common';
import { prisma, uniqueName, asyncExecShell, getServiceImage, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, supportedServiceTypesAndVersions, executeDockerCmd, listSettings, getFreeExposedPort, checkDomainsIsValidInDNS, persistentVolumes, asyncSleep, isARM, defaultComposeConfiguration, checkExposedPort } from '../../../../lib/common';
import { day } from '../../../../lib/dayjs';
import { checkContainer, isContainerExited, removeContainer } from '../../../../lib/docker';
import cuid from 'cuid';
import type { OnlyId } from '../../../../types';
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetWordpressSettings } from './types';
import { defaultServiceComposeConfiguration, defaultServiceConfigurations } from '../../../../lib/services';
import { defaultServiceConfigurations } from '../../../../lib/services';
// async function startServiceNew(request: FastifyRequest<OnlyId>) {
// try {
@@ -378,18 +378,7 @@ export async function checkService(request: FastifyRequest<CheckService>) {
}
}
}
if (exposePort) {
if (exposePort < 1024 || exposePort > 65535) {
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
}
if (configuredPort !== exposePort) {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }
}
}
}
await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress })
if (isDNSCheckEnabled && !isDev && !forceSave) {
let hostname = request.hostname.split(':')[0];
if (remoteEngine) hostname = remoteIpAddress;
@@ -458,13 +447,13 @@ export async function saveServiceSecret(request: FastifyRequest<SaveServiceSecre
if (found) {
throw `Secret ${name} already exists.`
} else {
value = encrypt(value);
value = encrypt(value.trim());
await prisma.serviceSecret.create({
data: { name, value, service: { connect: { id } } }
});
}
} else {
value = encrypt(value);
value = encrypt(value.trim());
const found = await prisma.serviceSecret.findFirst({ where: { serviceId: id, name } });
if (found) {
@@ -812,21 +801,21 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
labels: makeLabelForServices('plausibleAnalytics'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-postgresql`]: {
container_name: `${id}-postgresql`,
image: config.postgresql.image,
environment: config.postgresql.environmentVariables,
volumes: [config.postgresql.volume],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-clickhouse`]: {
build: workdir,
container_name: `${id}-clickhouse`,
environment: config.clickhouse.environmentVariables,
volumes: [config.clickhouse.volume],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -887,7 +876,7 @@ async function startNocodbService(request: FastifyRequest<ServiceStartStop>) {
environment: config.environmentVariables,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('nocodb'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -959,7 +948,7 @@ async function startMinioService(request: FastifyRequest<ServiceStartStop>) {
volumes,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('minio'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1025,7 +1014,7 @@ async function startVscodeService(request: FastifyRequest<ServiceStartStop>) {
volumes,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('vscodeServer'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1132,7 +1121,7 @@ async function startWordpressService(request: FastifyRequest<ServiceStartStop>)
volumes,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('wordpress'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1149,7 +1138,7 @@ async function startWordpressService(request: FastifyRequest<ServiceStartStop>)
image: config.mysql.image,
volumes: [config.mysql.volume],
environment: config.mysql.environmentVariables,
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
};
composeFile.volumes[config.mysql.volume.split(':')[0]] = {
@@ -1202,7 +1191,7 @@ async function startVaultwardenService(request: FastifyRequest<ServiceStartStop>
volumes,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('vaultWarden'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1258,7 +1247,7 @@ async function startLanguageToolService(request: FastifyRequest<ServiceStartStop
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
volumes,
labels: makeLabelForServices('languagetool'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1315,7 +1304,7 @@ async function startN8nService(request: FastifyRequest<ServiceStartStop>) {
environment: config.environmentVariables,
labels: makeLabelForServices('n8n'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1370,7 +1359,7 @@ async function startUptimekumaService(request: FastifyRequest<ServiceStartStop>)
environment: config.environmentVariables,
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('uptimekuma'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1469,14 +1458,14 @@ async function startGhostService(request: FastifyRequest<ServiceStartStop>) {
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('ghost'),
depends_on: [`${id}-mariadb`],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-mariadb`]: {
container_name: `${id}-mariadb`,
image: config.mariadb.image,
volumes: [config.mariadb.volume],
environment: config.mariadb.environmentVariables,
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1542,7 +1531,7 @@ async function startMeilisearchService(request: FastifyRequest<ServiceStartStop>
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
volumes,
labels: makeLabelForServices('meilisearch'),
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1708,14 +1697,14 @@ async function startUmamiService(request: FastifyRequest<ServiceStartStop>) {
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('umami'),
depends_on: [`${id}-postgresql`],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-postgresql`]: {
build: workdir,
container_name: `${id}-postgresql`,
environment: config.postgresql.environmentVariables,
volumes: [config.postgresql.volume],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1795,14 +1784,14 @@ async function startHasuraService(request: FastifyRequest<ServiceStartStop>) {
labels: makeLabelForServices('hasura'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
depends_on: [`${id}-postgresql`],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-postgresql`]: {
image: config.postgresql.image,
container_name: `${id}-postgresql`,
environment: config.postgresql.environmentVariables,
volumes: [config.postgresql.volume],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -1908,14 +1897,14 @@ async function startFiderService(request: FastifyRequest<ServiceStartStop>) {
labels: makeLabelForServices('fider'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
depends_on: [`${id}-postgresql`],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-postgresql`]: {
image: config.postgresql.image,
container_name: `${id}-postgresql`,
environment: config.postgresql.environmentVariables,
volumes: [config.postgresql.volume],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
},
networks: {
@@ -2001,7 +1990,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_STATSD_PORT=8125",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-realtime`]: {
image: `${image}:${version}`,
@@ -2024,7 +2013,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-audits`]: {
@@ -2048,7 +2037,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-webhooks`]: {
image: `${image}:${version}`,
@@ -2066,7 +2055,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_REDIS_PORT=6379",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-deletes`]: {
image: `${image}:${version}`,
@@ -2099,7 +2088,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-databases`]: {
image: `${image}:${version}`,
@@ -2122,7 +2111,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-builds`]: {
image: `${image}:${version}`,
@@ -2147,7 +2136,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-certificates`]: {
image: `${image}:${version}`,
@@ -2176,7 +2165,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-functions`]: {
image: `${image}:${version}`,
@@ -2202,7 +2191,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-executor`]: {
image: `${image}:${version}`,
@@ -2226,7 +2215,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_EXECUTOR_SECRET=${executorSecret}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-mails`]: {
image: `${image}:${version}`,
@@ -2243,7 +2232,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_REDIS_PORT=6379",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-worker-messaging`]: {
image: `${image}:${version}`,
@@ -2259,7 +2248,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_REDIS_PORT=6379",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-maintenance`]: {
image: `${image}:${version}`,
@@ -2283,7 +2272,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_DB_PASS=${mariadbPassword}`,
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-schedule`]: {
image: `${image}:${version}`,
@@ -2299,7 +2288,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_REDIS_PORT=6379",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-mariadb`]: {
"image": "mariadb:10.7",
@@ -2316,7 +2305,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`MYSQL_DATABASE=${mariadbDatabase}`
],
"command": "mysqld --innodb-flush-method=fsync",
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
[`${id}-redis`]: {
"image": "redis:6.2-alpine",
@@ -2325,7 +2314,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"volumes": [
`${id}-redis:/data:rw`
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
},
};
@@ -2354,7 +2343,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"_APP_REDIS_PORT=6379",
...secrets
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
dockerCompose[`${id}-influxdb`] = {
"image": "appwrite/influxdb:1.5.0",
@@ -2362,7 +2351,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
"volumes": [
`${id}-influxdb:/var/lib/influxdb:rw`
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
dockerCompose[`${id}-telegraf`] = {
"image": "appwrite/telegraf:1.4.0",
@@ -2371,7 +2360,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
`_APP_INFLUXDB_HOST=${id}-influxdb`,
"_APP_INFLUXDB_PORT=8086",
],
...defaultServiceComposeConfiguration(network),
...defaultComposeConfiguration(network),
}
}

View File

@@ -33,12 +33,13 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
minPort,
maxPort,
isAutoUpdateEnabled,
isDNSCheckEnabled
isDNSCheckEnabled,
DNSServers
} = request.body
const { id } = await listSettings();
await prisma.setting.update({
where: { id },
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled }
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers }
});
if (fqdn) {
await prisma.setting.update({ where: { id }, data: { fqdn } });
@@ -54,6 +55,10 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
export async function deleteDomain(request: FastifyRequest<DeleteDomain>, reply: FastifyReply) {
try {
const { fqdn } = request.body
const { DNSServers } = await listSettings();
if (DNSServers) {
dns.setServers([DNSServers]);
}
let ip;
try {
ip = await dns.resolve(fqdn);

View File

@@ -8,7 +8,8 @@ export interface SaveSettings {
minPort: number,
maxPort: number,
isAutoUpdateEnabled: boolean,
isDNSCheckEnabled: boolean
isDNSCheckEnabled: boolean,
DNSServers: string
}
}
export interface DeleteDomain {

View File

@@ -484,7 +484,6 @@ export async function traefikOtherConfiguration(request: FastifyRequest<TraefikO
}
throw { status: 500 }
} catch ({ status, message }) {
console.log(status, message);
return errorHandler({ status, message })
}
}