commit
becf37b676
@ -0,0 +1,24 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isCustomSSL" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isHttp2" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@ -186,6 +186,7 @@ model ApplicationSettings {
|
|||||||
isPublicRepository Boolean @default(false)
|
isPublicRepository Boolean @default(false)
|
||||||
isDBBranching Boolean @default(false)
|
isDBBranching Boolean @default(false)
|
||||||
isCustomSSL Boolean @default(false)
|
isCustomSSL Boolean @default(false)
|
||||||
|
isHttp2 Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
|
@ -53,12 +53,22 @@ export default async function (data) {
|
|||||||
value['environment'] = [...environment, ...envs];
|
value['environment'] = [...environment, ...envs];
|
||||||
|
|
||||||
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
|
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
|
||||||
if (Object.keys(build).length > 0) {
|
if (typeof build === 'string') {
|
||||||
build = Object.entries(build).map(([key, value]) => `${key}=${value}`);
|
build = { context: build };
|
||||||
|
}
|
||||||
|
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
|
||||||
|
let finalArgs = [...buildEnvs];
|
||||||
|
if (Object.keys(buildArgs).length > 0) {
|
||||||
|
for (const arg of buildArgs) {
|
||||||
|
const [key, _] = arg.split('=');
|
||||||
|
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
|
||||||
|
finalArgs.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value['build'] = {
|
value['build'] = {
|
||||||
...build,
|
...build,
|
||||||
args: [...(build?.args || []), ...buildEnvs]
|
args: finalArgs
|
||||||
};
|
};
|
||||||
|
|
||||||
value['labels'] = labels;
|
value['labels'] = labels;
|
||||||
|
@ -19,7 +19,7 @@ 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.11';
|
export const version = '3.12.12';
|
||||||
export const isDev = process.env.NODE_ENV === 'development';
|
export const isDev = process.env.NODE_ENV === 'development';
|
||||||
export const sentryDSN =
|
export const sentryDSN =
|
||||||
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
|
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
|
||||||
@ -714,8 +714,10 @@ export async function startTraefikProxy(id: string): Promise<void> {
|
|||||||
--network coolify-infra \
|
--network coolify-infra \
|
||||||
-p "80:80" \
|
-p "80:80" \
|
||||||
-p "443:443" \
|
-p "443:443" \
|
||||||
|
${isDev ? '-p "8080:8080"' : ''} \
|
||||||
--name coolify-proxy \
|
--name coolify-proxy \
|
||||||
-d ${defaultTraefikImage} \
|
-d ${defaultTraefikImage} \
|
||||||
|
${isDev ? '--api.insecure=true' : ''} \
|
||||||
--entrypoints.web.address=:80 \
|
--entrypoints.web.address=:80 \
|
||||||
--entrypoints.web.forwardedHeaders.insecure=true \
|
--entrypoints.web.forwardedHeaders.insecure=true \
|
||||||
--entrypoints.websecure.address=:443 \
|
--entrypoints.websecure.address=:443 \
|
||||||
|
@ -503,14 +503,24 @@ export async function saveApplicationSettings(
|
|||||||
projectId,
|
projectId,
|
||||||
isBot,
|
isBot,
|
||||||
isDBBranching,
|
isDBBranching,
|
||||||
isCustomSSL
|
isCustomSSL,
|
||||||
|
isHttp2
|
||||||
} = request.body;
|
} = request.body;
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
fqdn: isBot ? null : undefined,
|
fqdn: isBot ? null : undefined,
|
||||||
settings: {
|
settings: {
|
||||||
update: { debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL }
|
update: {
|
||||||
|
debug,
|
||||||
|
previews,
|
||||||
|
dualCerts,
|
||||||
|
autodeploy,
|
||||||
|
isBot,
|
||||||
|
isDBBranching,
|
||||||
|
isCustomSSL,
|
||||||
|
isHttp2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
include: { destinationDocker: true }
|
include: { destinationDocker: true }
|
||||||
|
@ -1,154 +1,170 @@
|
|||||||
import type { OnlyId } from "../../../../types";
|
import type { OnlyId } from '../../../../types';
|
||||||
|
|
||||||
export interface SaveApplication extends OnlyId {
|
export interface SaveApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
name: string,
|
name: string;
|
||||||
buildPack: string,
|
buildPack: string;
|
||||||
fqdn: string,
|
fqdn: string;
|
||||||
port: number,
|
port: number;
|
||||||
exposePort: number,
|
exposePort: number;
|
||||||
installCommand: string,
|
installCommand: string;
|
||||||
buildCommand: string,
|
buildCommand: string;
|
||||||
startCommand: string,
|
startCommand: string;
|
||||||
baseDirectory: string,
|
baseDirectory: string;
|
||||||
publishDirectory: string,
|
publishDirectory: string;
|
||||||
pythonWSGI: string,
|
pythonWSGI: string;
|
||||||
pythonModule: string,
|
pythonModule: string;
|
||||||
pythonVariable: string,
|
pythonVariable: string;
|
||||||
dockerFileLocation: string,
|
dockerFileLocation: string;
|
||||||
denoMainFile: string,
|
denoMainFile: string;
|
||||||
denoOptions: string,
|
denoOptions: string;
|
||||||
baseImage: string,
|
baseImage: string;
|
||||||
gitCommitHash: string,
|
gitCommitHash: string;
|
||||||
baseBuildImage: string,
|
baseBuildImage: string;
|
||||||
deploymentType: string,
|
deploymentType: string;
|
||||||
baseDatabaseBranch: string,
|
baseDatabaseBranch: string;
|
||||||
dockerComposeFile: string,
|
dockerComposeFile: string;
|
||||||
dockerComposeFileLocation: string,
|
dockerComposeFileLocation: string;
|
||||||
dockerComposeConfiguration: string,
|
dockerComposeConfiguration: string;
|
||||||
simpleDockerfile: string,
|
simpleDockerfile: string;
|
||||||
dockerRegistryImageName: string
|
dockerRegistryImageName: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSettings extends OnlyId {
|
export interface SaveApplicationSettings extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; isDBBranching: boolean, isCustomSSL: boolean };
|
Body: {
|
||||||
|
debug: boolean;
|
||||||
|
previews: boolean;
|
||||||
|
dualCerts: boolean;
|
||||||
|
autodeploy: boolean;
|
||||||
|
branch: string;
|
||||||
|
projectId: number;
|
||||||
|
isBot: boolean;
|
||||||
|
isDBBranching: boolean;
|
||||||
|
isCustomSSL: boolean;
|
||||||
|
isHttp2: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteApplication extends OnlyId {
|
export interface DeleteApplication extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: { force: boolean }
|
Body: { force: boolean };
|
||||||
}
|
}
|
||||||
export interface CheckDomain extends OnlyId {
|
export interface CheckDomain extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
}
|
}
|
||||||
export interface CheckDNS extends OnlyId {
|
export interface CheckDNS extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: {
|
Body: {
|
||||||
exposePort: number,
|
exposePort: number;
|
||||||
fqdn: string,
|
fqdn: string;
|
||||||
forceSave: boolean,
|
forceSave: boolean;
|
||||||
dualCerts: boolean
|
dualCerts: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeployApplication {
|
export interface DeployApplication {
|
||||||
Querystring: { domain: string }
|
Querystring: { domain: string };
|
||||||
Body: { pullmergeRequestId: string | null, branch: string, forceRebuild?: boolean }
|
Body: { pullmergeRequestId: string | null; branch: string; forceRebuild?: boolean };
|
||||||
}
|
}
|
||||||
export interface GetImages {
|
export interface GetImages {
|
||||||
Body: { buildPack: string, deploymentType: string }
|
Body: { buildPack: string; deploymentType: string };
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSource extends OnlyId {
|
export interface SaveApplicationSource extends OnlyId {
|
||||||
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string, simpleDockerfile?: string }
|
Body: {
|
||||||
|
gitSourceId?: string | null;
|
||||||
|
forPublic?: boolean;
|
||||||
|
type?: string;
|
||||||
|
simpleDockerfile?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface CheckRepository extends OnlyId {
|
export interface CheckRepository extends OnlyId {
|
||||||
Querystring: { repository: string, branch: string }
|
Querystring: { repository: string; branch: string };
|
||||||
}
|
}
|
||||||
export interface SaveDestination extends OnlyId {
|
export interface SaveDestination extends OnlyId {
|
||||||
Body: { destinationId: string }
|
Body: { destinationId: string };
|
||||||
}
|
}
|
||||||
export interface SaveSecret extends OnlyId {
|
export interface SaveSecret extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
name: string,
|
name: string;
|
||||||
value: string,
|
value: string;
|
||||||
isBuildSecret: boolean,
|
isBuildSecret: boolean;
|
||||||
previewSecret: boolean,
|
previewSecret: boolean;
|
||||||
isNew: boolean
|
isNew: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteSecret extends OnlyId {
|
export interface DeleteSecret extends OnlyId {
|
||||||
Body: { name: string }
|
Body: { name: string };
|
||||||
}
|
}
|
||||||
export interface SaveStorage extends OnlyId {
|
export interface SaveStorage extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
path: string,
|
path: string;
|
||||||
newStorage: boolean,
|
newStorage: boolean;
|
||||||
storageId: string
|
storageId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteStorage extends OnlyId {
|
export interface DeleteStorage extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
path: string,
|
path: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface GetApplicationLogs {
|
export interface GetApplicationLogs {
|
||||||
Params: {
|
Params: {
|
||||||
id: string,
|
id: string;
|
||||||
containerId: string
|
containerId: string;
|
||||||
}
|
};
|
||||||
Querystring: {
|
Querystring: {
|
||||||
since: number,
|
since: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface GetBuilds extends OnlyId {
|
export interface GetBuilds extends OnlyId {
|
||||||
Querystring: {
|
Querystring: {
|
||||||
buildId: string
|
buildId: string;
|
||||||
skip: number,
|
skip: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface GetBuildIdLogs {
|
export interface GetBuildIdLogs {
|
||||||
Params: {
|
Params: {
|
||||||
id: string,
|
id: string;
|
||||||
buildId: string
|
buildId: string;
|
||||||
},
|
};
|
||||||
Querystring: {
|
Querystring: {
|
||||||
sequence: number
|
sequence: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface SaveDeployKey extends OnlyId {
|
export interface SaveDeployKey extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
deployKeyId: number
|
deployKeyId: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface CancelDeployment {
|
export interface CancelDeployment {
|
||||||
Body: {
|
Body: {
|
||||||
buildId: string,
|
buildId: string;
|
||||||
applicationId: string
|
applicationId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeployApplication extends OnlyId {
|
export interface DeployApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null;
|
||||||
branch: string,
|
branch: string;
|
||||||
forceRebuild?: boolean
|
forceRebuild?: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StopPreviewApplication extends OnlyId {
|
export interface StopPreviewApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface RestartPreviewApplication {
|
export interface RestartPreviewApplication {
|
||||||
Params: {
|
Params: {
|
||||||
id: string,
|
id: string;
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface RestartApplication {
|
export interface RestartApplication {
|
||||||
Params: {
|
Params: {
|
||||||
id: string,
|
id: string;
|
||||||
},
|
};
|
||||||
Body: {
|
Body: {
|
||||||
imageId: string | null,
|
imageId: string | null;
|
||||||
}
|
};
|
||||||
}
|
}
|
@ -1,9 +1,31 @@
|
|||||||
import { FastifyRequest } from "fastify";
|
import { FastifyRequest } from 'fastify';
|
||||||
import { errorHandler, getDomain, isDev, prisma, executeCommand } from "../../../lib/common";
|
import { errorHandler, getDomain, isDev, prisma, executeCommand } from '../../../lib/common';
|
||||||
import { getTemplates } from "../../../lib/services";
|
import { getTemplates } from '../../../lib/services';
|
||||||
import { OnlyId } from "../../../types";
|
import { OnlyId } from '../../../types';
|
||||||
|
|
||||||
function generateServices(serviceId, containerId, port) {
|
function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps = false) {
|
||||||
|
if (isHttp2) {
|
||||||
|
return {
|
||||||
|
[serviceId]: {
|
||||||
|
loadbalancer: {
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: `${isHttps ? 'https' : 'http'}://${containerId}:${port}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[`${serviceId}-http2`]: {
|
||||||
|
loadbalancer: {
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: `h2c://${containerId}:${port}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
[serviceId]: {
|
[serviceId]: {
|
||||||
loadbalancer: {
|
loadbalancer: {
|
||||||
@ -14,43 +36,54 @@ function generateServices(serviceId, containerId, port) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, isDualCerts, isCustomSSL) {
|
function generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
isDualCerts,
|
||||||
|
isCustomSSL,
|
||||||
|
isHttp2 = false
|
||||||
|
) {
|
||||||
|
let rule = `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`;
|
||||||
let http: any = {
|
let http: any = {
|
||||||
entrypoints: ['web'],
|
entrypoints: ['web'],
|
||||||
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule,
|
||||||
service: `${serviceId}`,
|
service: `${serviceId}`,
|
||||||
priority: 2,
|
priority: 2,
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
};
|
||||||
let https: any = {
|
let https: any = {
|
||||||
entrypoints: ['websecure'],
|
entrypoints: ['websecure'],
|
||||||
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule,
|
||||||
service: `${serviceId}`,
|
service: `${serviceId}`,
|
||||||
priority: 2,
|
priority: 2,
|
||||||
tls: {
|
tls: {
|
||||||
certresolver: 'letsencrypt'
|
certresolver: 'letsencrypt'
|
||||||
},
|
},
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
};
|
||||||
let httpWWW: any = {
|
let httpWWW: any = {
|
||||||
entrypoints: ['web'],
|
entrypoints: ['web'],
|
||||||
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule,
|
||||||
service: `${serviceId}`,
|
service: `${serviceId}`,
|
||||||
priority: 2,
|
priority: 2,
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
};
|
||||||
let httpsWWW: any = {
|
let httpsWWW: any = {
|
||||||
entrypoints: ['websecure'],
|
entrypoints: ['websecure'],
|
||||||
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule,
|
||||||
service: `${serviceId}`,
|
service: `${serviceId}`,
|
||||||
priority: 2,
|
priority: 2,
|
||||||
tls: {
|
tls: {
|
||||||
certresolver: 'letsencrypt'
|
certresolver: 'letsencrypt'
|
||||||
},
|
},
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
};
|
||||||
// 2. http + non-www only
|
// 2. http + non-www only
|
||||||
if (!isHttps && !isWWW) {
|
if (!isHttps && !isWWW) {
|
||||||
https.middlewares.push('redirect-to-http');
|
https.middlewares.push('redirect-to-http');
|
||||||
@ -58,8 +91,8 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
|
|||||||
|
|
||||||
httpWWW.middlewares.push('redirect-to-non-www');
|
httpWWW.middlewares.push('redirect-to-non-www');
|
||||||
httpsWWW.middlewares.push('redirect-to-non-www');
|
httpsWWW.middlewares.push('redirect-to-non-www');
|
||||||
delete https.tls
|
delete https.tls;
|
||||||
delete httpsWWW.tls
|
delete httpsWWW.tls;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. http + www only
|
// 3. http + www only
|
||||||
@ -69,8 +102,8 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
|
|||||||
|
|
||||||
http.middlewares.push('redirect-to-www');
|
http.middlewares.push('redirect-to-www');
|
||||||
https.middlewares.push('redirect-to-www');
|
https.middlewares.push('redirect-to-www');
|
||||||
delete https.tls
|
delete https.tls;
|
||||||
delete httpsWWW.tls
|
delete httpsWWW.tls;
|
||||||
}
|
}
|
||||||
// 5. https + non-www only
|
// 5. https + non-www only
|
||||||
if (isHttps && !isWWW) {
|
if (isHttps && !isWWW) {
|
||||||
@ -86,17 +119,17 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
|
|||||||
httpsWWW.tls = true;
|
httpsWWW.tls = true;
|
||||||
} else {
|
} else {
|
||||||
https.tls = true;
|
https.tls = true;
|
||||||
delete httpsWWW.tls.certresolver
|
delete httpsWWW.tls.certresolver;
|
||||||
httpsWWW.tls.domains = {
|
httpsWWW.tls.domains = {
|
||||||
main: domain
|
main: domain
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isDualCerts) {
|
if (!isDualCerts) {
|
||||||
delete httpsWWW.tls.certresolver
|
delete httpsWWW.tls.certresolver;
|
||||||
httpsWWW.tls.domains = {
|
httpsWWW.tls.domains = {
|
||||||
main: domain
|
main: domain
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,26 +147,59 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
|
|||||||
httpsWWW.tls = true;
|
httpsWWW.tls = true;
|
||||||
} else {
|
} else {
|
||||||
httpsWWW.tls = true;
|
httpsWWW.tls = true;
|
||||||
delete https.tls.certresolver
|
delete https.tls.certresolver;
|
||||||
https.tls.domains = {
|
https.tls.domains = {
|
||||||
main: domain
|
main: domain
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isDualCerts) {
|
if (!isDualCerts) {
|
||||||
delete https.tls.certresolver
|
delete https.tls.certresolver;
|
||||||
https.tls.domains = {
|
https.tls.domains = {
|
||||||
main: domain
|
main: domain
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isHttp2) {
|
||||||
|
let http2 = {
|
||||||
|
...http,
|
||||||
|
service: `${serviceId}-http2`,
|
||||||
|
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
|
||||||
|
};
|
||||||
|
let http2WWW = {
|
||||||
|
...httpWWW,
|
||||||
|
service: `${serviceId}-http2`,
|
||||||
|
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
|
||||||
|
};
|
||||||
|
let https2 = {
|
||||||
|
...https,
|
||||||
|
service: `${serviceId}-http2`,
|
||||||
|
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
|
||||||
|
};
|
||||||
|
|
||||||
|
let https2WWW = {
|
||||||
|
...httpsWWW,
|
||||||
|
service: `${serviceId}-http2`,
|
||||||
|
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
[`${serviceId}-${pathPrefix}`]: { ...http },
|
||||||
|
[`${serviceId}-${pathPrefix}-http2`]: { ...http2 },
|
||||||
|
[`${serviceId}-${pathPrefix}-secure`]: { ...https },
|
||||||
|
[`${serviceId}-${pathPrefix}-secure-http2`]: { ...https2 },
|
||||||
|
[`${serviceId}-${pathPrefix}-www`]: { ...httpWWW },
|
||||||
|
[`${serviceId}-${pathPrefix}-www-http2`]: { ...http2WWW },
|
||||||
|
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW },
|
||||||
|
[`${serviceId}-${pathPrefix}-secure-www-http2`]: { ...https2WWW }
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
[`${serviceId}-${pathPrefix}`]: { ...http },
|
[`${serviceId}-${pathPrefix}`]: { ...http },
|
||||||
[`${serviceId}-${pathPrefix}-secure`]: { ...https },
|
[`${serviceId}-${pathPrefix}-secure`]: { ...https },
|
||||||
[`${serviceId}-${pathPrefix}-www`]: { ...httpWWW },
|
[`${serviceId}-${pathPrefix}-www`]: { ...httpWWW },
|
||||||
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW },
|
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW }
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote: boolean = false) {
|
export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote: boolean = false) {
|
||||||
const traefik = {
|
const traefik = {
|
||||||
@ -174,26 +240,26 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
const coolifySettings = await prisma.setting.findFirst();
|
const coolifySettings = await prisma.setting.findFirst();
|
||||||
if (coolifySettings.isTraefikUsed && coolifySettings.proxyDefaultRedirect) {
|
if (coolifySettings.isTraefikUsed && coolifySettings.proxyDefaultRedirect) {
|
||||||
traefik.http.routers['catchall-http'] = {
|
traefik.http.routers['catchall-http'] = {
|
||||||
entrypoints: ["web"],
|
entrypoints: ['web'],
|
||||||
rule: "HostRegexp(`{catchall:.*}`)",
|
rule: 'HostRegexp(`{catchall:.*}`)',
|
||||||
service: "noop",
|
service: 'noop',
|
||||||
priority: 1,
|
priority: 1,
|
||||||
middlewares: ["redirect-regexp"]
|
middlewares: ['redirect-regexp']
|
||||||
}
|
};
|
||||||
traefik.http.routers['catchall-https'] = {
|
traefik.http.routers['catchall-https'] = {
|
||||||
entrypoints: ["websecure"],
|
entrypoints: ['websecure'],
|
||||||
rule: "HostRegexp(`{catchall:.*}`)",
|
rule: 'HostRegexp(`{catchall:.*}`)',
|
||||||
service: "noop",
|
service: 'noop',
|
||||||
priority: 1,
|
priority: 1,
|
||||||
middlewares: ["redirect-regexp"]
|
middlewares: ['redirect-regexp']
|
||||||
}
|
};
|
||||||
traefik.http.middlewares['redirect-regexp'] = {
|
traefik.http.middlewares['redirect-regexp'] = {
|
||||||
redirectregex: {
|
redirectregex: {
|
||||||
regex: '(.*)',
|
regex: '(.*)',
|
||||||
replacement: coolifySettings.proxyDefaultRedirect,
|
replacement: coolifySettings.proxyDefaultRedirect,
|
||||||
permanent: false
|
permanent: false
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
traefik.http.services['noop'] = {
|
traefik.http.services['noop'] = {
|
||||||
loadBalancer: {
|
loadBalancer: {
|
||||||
servers: [
|
servers: [
|
||||||
@ -202,25 +268,41 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
const sslpath = '/etc/traefik/acme/custom';
|
const sslpath = '/etc/traefik/acme/custom';
|
||||||
|
|
||||||
let certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } } } } })
|
let certificates = await prisma.certificate.findMany({
|
||||||
|
where: {
|
||||||
|
team: {
|
||||||
|
applications: { some: { settings: { isCustomSSL: true } } },
|
||||||
|
destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (remote) {
|
if (remote) {
|
||||||
certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { id, remoteEngine: true, isCoolifyProxyUsed: true, remoteVerified: true } } } } })
|
certificates = await prisma.certificate.findMany({
|
||||||
|
where: {
|
||||||
|
team: {
|
||||||
|
applications: { some: { settings: { isCustomSSL: true } } },
|
||||||
|
destinationDocker: {
|
||||||
|
some: { id, remoteEngine: true, isCoolifyProxyUsed: true, remoteVerified: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsedCertificates = []
|
let parsedCertificates = [];
|
||||||
for (const certificate of certificates) {
|
for (const certificate of certificates) {
|
||||||
parsedCertificates.push({
|
parsedCertificates.push({
|
||||||
certFile: `${sslpath}/${certificate.id}-cert.pem`,
|
certFile: `${sslpath}/${certificate.id}-cert.pem`,
|
||||||
keyFile: `${sslpath}/${certificate.id}-key.pem`
|
keyFile: `${sslpath}/${certificate.id}-key.pem`
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (parsedCertificates.length > 0) {
|
if (parsedCertificates.length > 0) {
|
||||||
traefik.tls.certificates = parsedCertificates
|
traefik.tls.certificates = parsedCertificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
let applications = [];
|
let applications = [];
|
||||||
@ -236,7 +318,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
destinationDocker: true,
|
destinationDocker: true,
|
||||||
persistentStorage: true,
|
persistentStorage: true,
|
||||||
serviceSecret: true,
|
serviceSecret: true,
|
||||||
serviceSetting: true,
|
serviceSetting: true
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'desc' }
|
orderBy: { createdAt: 'desc' }
|
||||||
});
|
});
|
||||||
@ -251,23 +333,25 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
destinationDocker: true,
|
destinationDocker: true,
|
||||||
persistentStorage: true,
|
persistentStorage: true,
|
||||||
serviceSecret: true,
|
serviceSecret: true,
|
||||||
serviceSetting: true,
|
serviceSetting: true
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (applications.length > 0) {
|
if (applications.length > 0) {
|
||||||
const dockerIds = new Set()
|
const dockerIds = new Set();
|
||||||
const runningContainers = {}
|
const runningContainers = {};
|
||||||
applications.forEach((app) => dockerIds.add(app.destinationDocker.id));
|
applications.forEach((app) => dockerIds.add(app.destinationDocker.id));
|
||||||
for (const dockerId of dockerIds) {
|
for (const dockerId of dockerIds) {
|
||||||
const { stdout: container } = await executeCommand({ dockerId, command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` })
|
const { stdout: container } = await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'`
|
||||||
|
});
|
||||||
if (container) {
|
if (container) {
|
||||||
const containersArray = container.trim().split('\n');
|
const containersArray = container.trim().split('\n');
|
||||||
if (containersArray.length > 0) {
|
if (containersArray.length > 0) {
|
||||||
runningContainers[dockerId] = containersArray
|
runningContainers[dockerId] = containersArray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,38 +373,54 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
if (
|
if (
|
||||||
!runningContainers[destinationDockerId] ||
|
!runningContainers[destinationDockerId] ||
|
||||||
runningContainers[destinationDockerId].length === 0 ||
|
runningContainers[destinationDockerId].length === 0 ||
|
||||||
runningContainers[destinationDockerId].filter((container) => container.startsWith(id)).length === 0
|
runningContainers[destinationDockerId].filter((container) => container.startsWith(id))
|
||||||
|
.length === 0
|
||||||
) {
|
) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
if (buildPack === 'compose') {
|
if (buildPack === 'compose') {
|
||||||
const services = Object.entries(JSON.parse(dockerComposeConfiguration))
|
const services = Object.entries(JSON.parse(dockerComposeConfiguration));
|
||||||
if (services.length > 0) {
|
if (services.length > 0) {
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
const [key, value] = service
|
const [key, value] = service;
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
if (!value.fqdn || !value.port) {
|
if (!value.fqdn || !value.port) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const { fqdn, port } = value
|
const { fqdn, port } = value;
|
||||||
const containerId = `${id}-${key}`
|
const containerId = `${id}-${key}`;
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
const isWWW = fqdn.includes('www.');
|
const isWWW = fqdn.includes('www.');
|
||||||
const pathPrefix = '/'
|
const pathPrefix = '/';
|
||||||
const isCustomSSL = false;
|
const isCustomSSL = false;
|
||||||
const dualCerts = false;
|
const dualCerts = false;
|
||||||
const serviceId = `${id}-${port || 'default'}`
|
const serviceId = `${id}-${port || 'default'}`;
|
||||||
|
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, containerId, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, containerId, port)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const { previews, dualCerts, isCustomSSL } = settings;
|
const { previews, dualCerts, isCustomSSL, isHttp2 } = settings;
|
||||||
const { network, id: dockerId } = destinationDocker;
|
const { network, id: dockerId } = destinationDocker;
|
||||||
if (!fqdn) {
|
if (!fqdn) {
|
||||||
continue;
|
continue;
|
||||||
@ -329,12 +429,31 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
const isWWW = fqdn.includes('www.');
|
const isWWW = fqdn.includes('www.');
|
||||||
const pathPrefix = '/'
|
const pathPrefix = '/';
|
||||||
const serviceId = `${id}-${port || 'default'}`
|
const serviceId = `${id}-${port || 'default'}`;
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL,
|
||||||
|
isHttp2
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, id, port, isHttp2, isHttps)
|
||||||
|
};
|
||||||
if (previews) {
|
if (previews) {
|
||||||
const { stdout } = await executeCommand({ dockerId, command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` })
|
const { stdout } = await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"`
|
||||||
|
});
|
||||||
if (stdout) {
|
if (stdout) {
|
||||||
const containers = stdout
|
const containers = stdout
|
||||||
.trim()
|
.trim()
|
||||||
@ -343,44 +462,57 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
.map((c) => c.replace(/"/g, ''));
|
.map((c) => c.replace(/"/g, ''));
|
||||||
if (containers.length > 0) {
|
if (containers.length > 0) {
|
||||||
for (const container of containers) {
|
for (const container of containers) {
|
||||||
const previewDomain = `${container.split('-')[1]}${coolifySettings.previewSeparator}${domain}`;
|
const previewDomain = `${container.split('-')[1]}${
|
||||||
|
coolifySettings.previewSeparator
|
||||||
|
}${domain}`;
|
||||||
const nakedDomain = previewDomain.replace(/^www\./, '');
|
const nakedDomain = previewDomain.replace(/^www\./, '');
|
||||||
const pathPrefix = '/'
|
const pathPrefix = '/';
|
||||||
const serviceId = `${container}-${port || 'default'}`
|
const serviceId = `${container}-${port || 'default'}`;
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, previewDomain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
previewDomain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, container, port, isHttp2)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (services.length > 0) {
|
if (services.length > 0) {
|
||||||
const dockerIds = new Set()
|
const dockerIds = new Set();
|
||||||
const runningContainers = {}
|
const runningContainers = {};
|
||||||
services.forEach((app) => dockerIds.add(app.destinationDocker.id));
|
services.forEach((app) => dockerIds.add(app.destinationDocker.id));
|
||||||
for (const dockerId of dockerIds) {
|
for (const dockerId of dockerIds) {
|
||||||
const { stdout: container } = await executeCommand({ dockerId, command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` })
|
const { stdout: container } = await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'`
|
||||||
|
});
|
||||||
if (container) {
|
if (container) {
|
||||||
const containersArray = container.trim().split('\n');
|
const containersArray = container.trim().split('\n');
|
||||||
if (containersArray.length > 0) {
|
if (containersArray.length > 0) {
|
||||||
runningContainers[dockerId] = containersArray
|
runningContainers[dockerId] = containersArray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
try {
|
try {
|
||||||
let {
|
let { fqdn, id, type, destinationDockerId, dualCerts, serviceSetting } = service;
|
||||||
fqdn,
|
|
||||||
id,
|
|
||||||
type,
|
|
||||||
destinationDockerId,
|
|
||||||
dualCerts,
|
|
||||||
serviceSetting
|
|
||||||
} = service;
|
|
||||||
if (!fqdn) {
|
if (!fqdn) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -392,7 +524,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
runningContainers[destinationDockerId].length === 0 ||
|
runningContainers[destinationDockerId].length === 0 ||
|
||||||
!runningContainers[destinationDockerId].includes(id)
|
!runningContainers[destinationDockerId].includes(id)
|
||||||
) {
|
) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
const templates = await getTemplates();
|
const templates = await getTemplates();
|
||||||
let found = templates.find((a) => a.type === type);
|
let found = templates.find((a) => a.type === type);
|
||||||
@ -401,88 +533,143 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
}
|
}
|
||||||
found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id));
|
found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id));
|
||||||
for (const oneService of Object.keys(found.services)) {
|
for (const oneService of Object.keys(found.services)) {
|
||||||
const isDomainConfiguration = found?.services[oneService]?.proxy?.filter(p => p.domain) ?? [];
|
const isDomainConfiguration =
|
||||||
|
found?.services[oneService]?.proxy?.filter((p) => p.domain) ?? [];
|
||||||
if (isDomainConfiguration.length > 0) {
|
if (isDomainConfiguration.length > 0) {
|
||||||
const { proxy } = found.services[oneService];
|
const { proxy } = found.services[oneService];
|
||||||
for (let configuration of proxy) {
|
for (let configuration of proxy) {
|
||||||
if (configuration.domain) {
|
if (configuration.domain) {
|
||||||
const setting = serviceSetting.find((a) => a.variableName === configuration.domain);
|
const setting = serviceSetting.find(
|
||||||
|
(a) => a.variableName === configuration.domain
|
||||||
|
);
|
||||||
if (setting) {
|
if (setting) {
|
||||||
configuration.domain = configuration.domain.replace(configuration.domain, setting.value);
|
configuration.domain = configuration.domain.replace(
|
||||||
|
configuration.domain,
|
||||||
|
setting.value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
|
const foundPortVariable = serviceSetting.find(
|
||||||
|
(a) => a.name.toLowerCase() === 'port'
|
||||||
|
);
|
||||||
if (foundPortVariable) {
|
if (foundPortVariable) {
|
||||||
configuration.port = foundPortVariable.value
|
configuration.port = foundPortVariable.value;
|
||||||
}
|
}
|
||||||
let port, pathPrefix, customDomain;
|
let port, pathPrefix, customDomain;
|
||||||
if (configuration) {
|
if (configuration) {
|
||||||
port = configuration?.port;
|
port = configuration?.port;
|
||||||
pathPrefix = configuration?.pathPrefix || '/';
|
pathPrefix = configuration?.pathPrefix || '/';
|
||||||
customDomain = configuration?.domain
|
customDomain = configuration?.domain;
|
||||||
}
|
}
|
||||||
if (customDomain) {
|
if (customDomain) {
|
||||||
fqdn = customDomain
|
fqdn = customDomain;
|
||||||
} else {
|
} else {
|
||||||
fqdn = service.fqdn
|
fqdn = service.fqdn;
|
||||||
}
|
}
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
const isWWW = fqdn.includes('www.');
|
const isWWW = fqdn.includes('www.');
|
||||||
const isCustomSSL = false;
|
const isCustomSSL = false;
|
||||||
const serviceId = `${oneService}-${port || 'default'}`
|
const serviceId = `${oneService}-${port || 'default'}`;
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, oneService, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, oneService, port)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (found.services[oneService].ports && found.services[oneService].ports.length > 0) {
|
if (found.services[oneService].ports && found.services[oneService].ports.length > 0) {
|
||||||
for (let [index, port] of found.services[oneService].ports.entries()) {
|
for (let [index, port] of found.services[oneService].ports.entries()) {
|
||||||
if (port == 22) continue;
|
if (port == 22) continue;
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
|
const foundPortVariable = serviceSetting.find(
|
||||||
|
(a) => a.name.toLowerCase() === 'port'
|
||||||
|
);
|
||||||
if (foundPortVariable) {
|
if (foundPortVariable) {
|
||||||
port = foundPortVariable.value
|
port = foundPortVariable.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
const isWWW = fqdn.includes('www.');
|
const isWWW = fqdn.includes('www.');
|
||||||
const pathPrefix = '/'
|
const pathPrefix = '/';
|
||||||
const isCustomSSL = false
|
const isCustomSSL = false;
|
||||||
const serviceId = `${oneService}-${port || 'default'}`
|
const serviceId = `${oneService}-${port || 'default'}`;
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, id, port)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!remote) {
|
if (!remote) {
|
||||||
const { fqdn, dualCerts } = await prisma.setting.findFirst();
|
const { fqdn, dualCerts } = await prisma.setting.findFirst();
|
||||||
if (!fqdn) {
|
if (!fqdn) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
const isWWW = fqdn.includes('www.');
|
const isWWW = fqdn.includes('www.');
|
||||||
const id = isDev ? 'host.docker.internal' : 'coolify'
|
const id = isDev ? 'host.docker.internal' : 'coolify';
|
||||||
const container = isDev ? 'host.docker.internal' : 'coolify'
|
const container = isDev ? 'host.docker.internal' : 'coolify';
|
||||||
const port = 3000
|
const port = 3000;
|
||||||
const pathPrefix = '/'
|
const pathPrefix = '/';
|
||||||
const isCustomSSL = false
|
const isCustomSSL = false;
|
||||||
const serviceId = `${id}-${port || 'default'}`
|
const serviceId = `${id}-${port || 'default'}`;
|
||||||
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
|
traefik.http.routers = {
|
||||||
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
|
...traefik.http.routers,
|
||||||
|
...generateRouters(
|
||||||
|
serviceId,
|
||||||
|
domain,
|
||||||
|
nakedDomain,
|
||||||
|
pathPrefix,
|
||||||
|
isHttps,
|
||||||
|
isWWW,
|
||||||
|
dualCerts,
|
||||||
|
isCustomSSL
|
||||||
|
)
|
||||||
|
};
|
||||||
|
traefik.http.services = {
|
||||||
|
...traefik.http.services,
|
||||||
|
...generateServices(serviceId, container, port)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
if (Object.keys(traefik.http.routers).length === 0) {
|
if (Object.keys(traefik.http.routers).length === 0) {
|
||||||
traefik.http.routers = null;
|
traefik.http.routers = null;
|
||||||
@ -496,9 +683,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
|
|
||||||
export async function otherProxyConfiguration(request: FastifyRequest<TraefikOtherConfiguration>) {
|
export async function otherProxyConfiguration(request: FastifyRequest<TraefikOtherConfiguration>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.query
|
const { id } = request.query;
|
||||||
if (id) {
|
if (id) {
|
||||||
const { privatePort, publicPort, type, address = id } = request.query
|
const { privatePort, publicPort, type, address = id } = request.query;
|
||||||
let traefik = {};
|
let traefik = {};
|
||||||
if (publicPort && type && privatePort) {
|
if (publicPort && type && privatePort) {
|
||||||
if (type === 'tcp') {
|
if (type === 'tcp') {
|
||||||
@ -559,18 +746,18 @@ export async function otherProxyConfiguration(request: FastifyRequest<TraefikOth
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw { status: 500 }
|
throw { status: 500 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw { status: 500 }
|
throw { status: 500 };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...traefik
|
...traefik
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw { status: 500 }
|
throw { status: 500 };
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
let isBot = application.settings?.isBot;
|
let isBot = application.settings?.isBot;
|
||||||
let isDBBranching = application.settings?.isDBBranching;
|
let isDBBranching = application.settings?.isDBBranching;
|
||||||
let htmlUrl = application.gitSource?.htmlUrl;
|
let htmlUrl = application.gitSource?.htmlUrl;
|
||||||
|
let isHttp2 = application.settings?.isHttp2;
|
||||||
|
|
||||||
let dockerComposeFile = JSON.parse(application.dockerComposeFile) || null;
|
let dockerComposeFile = JSON.parse(application.dockerComposeFile) || null;
|
||||||
let dockerComposeServices: any[] = [];
|
let dockerComposeServices: any[] = [];
|
||||||
@ -177,6 +178,9 @@
|
|||||||
if (name === 'isDBBranching') {
|
if (name === 'isDBBranching') {
|
||||||
isDBBranching = !isDBBranching;
|
isDBBranching = !isDBBranching;
|
||||||
}
|
}
|
||||||
|
if (name === 'isHttp2') {
|
||||||
|
isHttp2 = !isHttp2;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await trpc.applications.saveSettings.mutate({
|
await trpc.applications.saveSettings.mutate({
|
||||||
id: application.id,
|
id: application.id,
|
||||||
@ -713,7 +717,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center pb-4">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
id="dualCerts"
|
id="dualCerts"
|
||||||
dataTooltip="Must be stopped to modify."
|
dataTooltip="Must be stopped to modify."
|
||||||
@ -737,6 +741,16 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="grid grid-cols-2 items-center pb-4">
|
||||||
|
<Setting
|
||||||
|
id="isHttp2"
|
||||||
|
isCenter={false}
|
||||||
|
bind:setting={isHttp2}
|
||||||
|
title="Enable HTTP/2 protocol?"
|
||||||
|
description="Enable HTTP/2 protocol. <br><br>HTTP/2 is a major revision of the HTTP network protocol used by the World Wide Web that allows faster web page loading by reducing the number of requests needed to load a web page.<br><br>Useful for gRPC and other HTTP/2 based services."
|
||||||
|
on:click={() => changeSettings('isHttp2')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if isSimpleDockerfile}
|
{#if isSimpleDockerfile}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isCustomSSL" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isHttp2" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@ -186,6 +186,7 @@ model ApplicationSettings {
|
|||||||
isPublicRepository Boolean @default(false)
|
isPublicRepository Boolean @default(false)
|
||||||
isDBBranching Boolean @default(false)
|
isDBBranching Boolean @default(false)
|
||||||
isCustomSSL Boolean @default(false)
|
isCustomSSL Boolean @default(false)
|
||||||
|
isHttp2 Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
|
@ -55,12 +55,22 @@ export default async function (data) {
|
|||||||
value['environment'] = [...environment, ...envs];
|
value['environment'] = [...environment, ...envs];
|
||||||
|
|
||||||
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
|
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
|
||||||
if (Object.keys(build).length > 0) {
|
if (typeof build === 'string') {
|
||||||
build = Object.entries(build).map(([key, value]) => `${key}=${value}`);
|
build = { context: build };
|
||||||
|
}
|
||||||
|
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
|
||||||
|
let finalArgs = [...buildEnvs];
|
||||||
|
if (Object.keys(buildArgs).length > 0) {
|
||||||
|
for (const arg of buildArgs) {
|
||||||
|
const [key, _] = arg.split('=');
|
||||||
|
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
|
||||||
|
finalArgs.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value['build'] = {
|
value['build'] = {
|
||||||
...build,
|
...build,
|
||||||
args: [...(build?.args || []), ...buildEnvs]
|
args: finalArgs
|
||||||
};
|
};
|
||||||
|
|
||||||
value['labels'] = labels;
|
value['labels'] = labels;
|
||||||
|
@ -808,18 +808,19 @@ export const applicationsRouter = router({
|
|||||||
isBot: z.boolean().optional(),
|
isBot: z.boolean().optional(),
|
||||||
autodeploy: z.boolean().optional(),
|
autodeploy: z.boolean().optional(),
|
||||||
isDBBranching: z.boolean().optional(),
|
isDBBranching: z.boolean().optional(),
|
||||||
isCustomSSL: z.boolean().optional()
|
isCustomSSL: z.boolean().optional(),
|
||||||
|
isHttp2: z.boolean().optional()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const { id, debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL } =
|
const { id, debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL, isHttp2 } =
|
||||||
input;
|
input;
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
fqdn: isBot ? null : undefined,
|
fqdn: isBot ? null : undefined,
|
||||||
settings: {
|
settings: {
|
||||||
update: { debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL }
|
update: { debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL, isHttp2 }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
include: { destinationDocker: true }
|
include: { destinationDocker: true }
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
let isBot = application.settings?.isBot;
|
let isBot = application.settings?.isBot;
|
||||||
let isDBBranching = application.settings?.isDBBranching;
|
let isDBBranching = application.settings?.isDBBranching;
|
||||||
let htmlUrl = application.gitSource?.htmlUrl;
|
let htmlUrl = application.gitSource?.htmlUrl;
|
||||||
|
let isHttp2 = application.settings.isHttp2;
|
||||||
|
|
||||||
let dockerComposeFile = JSON.parse(application.dockerComposeFile) || null;
|
let dockerComposeFile = JSON.parse(application.dockerComposeFile) || null;
|
||||||
let dockerComposeServices: any[] = [];
|
let dockerComposeServices: any[] = [];
|
||||||
@ -195,6 +196,9 @@
|
|||||||
if (name === 'isDBBranching') {
|
if (name === 'isDBBranching') {
|
||||||
isDBBranching = !isDBBranching;
|
isDBBranching = !isDBBranching;
|
||||||
}
|
}
|
||||||
|
if (name === 'isHttp2') {
|
||||||
|
isHttp2 = !isHttp2;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await post(`/applications/${id}/settings`, {
|
await post(`/applications/${id}/settings`, {
|
||||||
previews,
|
previews,
|
||||||
@ -204,6 +208,7 @@
|
|||||||
autodeploy,
|
autodeploy,
|
||||||
isDBBranching,
|
isDBBranching,
|
||||||
isCustomSSL,
|
isCustomSSL,
|
||||||
|
isHttp2,
|
||||||
branch: application.branch,
|
branch: application.branch,
|
||||||
projectId: application.projectId
|
projectId: application.projectId
|
||||||
});
|
});
|
||||||
@ -734,7 +739,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center pb-4">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
id="dualCerts"
|
id="dualCerts"
|
||||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||||
@ -746,6 +751,7 @@
|
|||||||
on:click={() => !isDisabled && changeSettings('dualCerts')}
|
on:click={() => !isDisabled && changeSettings('dualCerts')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if isHttps && application.buildPack !== 'compose'}
|
{#if isHttps && application.buildPack !== 'compose'}
|
||||||
<div class="grid grid-cols-2 items-center pb-4">
|
<div class="grid grid-cols-2 items-center pb-4">
|
||||||
<Setting
|
<Setting
|
||||||
@ -758,6 +764,16 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="grid grid-cols-2 items-center pb-4">
|
||||||
|
<Setting
|
||||||
|
id="isHttp2"
|
||||||
|
isCenter={false}
|
||||||
|
bind:setting={isHttp2}
|
||||||
|
title="Enable HTTP/2 protocol?"
|
||||||
|
description="Enable HTTP/2 protocol. <br><br>HTTP/2 is a major revision of the HTTP network protocol used by the World Wide Web that allows faster web page loading by reducing the number of requests needed to load a web page.<br><br>Useful for gRPC and other HTTP/2 based services."
|
||||||
|
on:click={() => changeSettings('isHttp2')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if isSimpleDockerfile}
|
{#if isSimpleDockerfile}
|
||||||
|
@ -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.11",
|
"version": "3.12.12",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user