fix: no variables in template

feat: hostPort proxy conf from template
This commit is contained in:
Andras Bacsai 2022-11-18 14:28:05 +01:00
parent ca05828b68
commit aac6981304
10 changed files with 220 additions and 102 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,73 @@
- templateVersion: 1.0.0
defaultVersion: '20.0'
ignore: true
defaultVersion: "1.17"
documentation: https://docs.gitea.io
type: gitea
name: Gitea
description: Gitea is a community managed lightweight code hosting solution written in Go.
labels:
- storage
- git
services:
$$id:
name: Gitea
documentation: https://docs.gitea.io
image: gitea/gitea:$$core_version
volumes:
- $$id-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=1000
- USER_GID=1000
- DOMAIN=$$config_domain
- SSH_DOMAIN=$$config_ssh_domain
- ROOT_URL=$$config_root_url
- SECRET_KEY=$$secret_secret_key
- INTERNAL_TOKEN=$$secret_internal_token
- SSH_PORT=$$config_hostport_ssh
ports:
- "3000"
- "22"
proxy:
- port: "22"
hostPort: $$config_hostport_ssh
variables:
- id: $$config_hostport_ssh
name: SSH_PORT
label: SSH Port
defaultValue: "8022"
description: ""
required: true
- id: $$config_domain
name: DOMAIN
label: Domain
defaultValue: $$generate_domain
description: ""
- id: $$config_ssh_domain
name: SSH_DOMAIN
label: SSH Domain
defaultValue: $$generate_domain
description: ""
- id: $$config_root_url
name: ROOT_URL
label: Root URL of Gitea
defaultValue: $$generate_fqdn_slash
description: ""
- id: $$secret_secret_key
name: SECRET_KEY
label: Secret Key
defaultValue: $$generate_hex(32)
description: ""
showOnConfiguration: true
- id: $$secret_internal_token
name: INTERNAL_TOKEN
label: Internal JWT Token
defaultValue: $$generate_token
description: ""
showOnConfiguration: true
- templateVersion: 1.0.0
defaultVersion: "20.0"
documentation: https://www.keycloak.org/documentation
type: keycloak
name: Keycloak
@ -12,7 +80,7 @@
services:
$$id:
name: Keycloak
command: start --db=postgres
command: start --db=postgres --features=token-exchange --import-realm
depends_on:
- $$id-postgresql
image: "quay.io/keycloak/keycloak:$$core_version"
@ -29,7 +97,7 @@
- KC_DB_USERNAME=$$config_postgres_user
- KC_DB_URL=$$secret_keycloak_database_url
ports:
- '8080'
- "8080"
$$id-postgresql:
name: PostgreSQL
depends_on: []
@ -138,7 +206,7 @@
- id: $$config_port
name: PORT
label: Port
defaultValue: '2333'
defaultValue: "2333"
required: true
- id: $$secret_password
name: PASSWORD
@ -2243,6 +2311,7 @@
description: ""
showOnConfiguration: true
- templateVersion: 1.0.0
ignore: true
defaultVersion: latest
documentation: https://docs.ghost.org
type: ghost-mariadb

View File

@ -1,5 +1,5 @@
import cuid from "cuid";
import { decrypt, encrypt, fixType, generatePassword, prisma } from "./lib/common";
import { decrypt, encrypt, fixType, generatePassword, generateToken, prisma } from "./lib/common";
import { getTemplates } from "./lib/services";
export async function migrateApplicationPersistentStorage() {
@ -83,39 +83,42 @@ export async function migrateServicesToNewTemplate() {
} catch (error) {
console.log(error)
}
if (template.variables.length > 0) {
if (template.variables) {
if (template.variables.length > 0) {
for (const variable of template.variables) {
const { defaultValue } = variable;
const regex = /^\$\$.*\((\d+)\)$/g;
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
if (variable.defaultValue.startsWith('$$generate_password')) {
variable.value = generatePassword({ length });
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid();
} else if (variable.defaultValue.startsWith('$$generate_token')) {
variable.value = generateToken()
} else {
variable.value = variable.defaultValue || '';
}
}
}
for (const variable of template.variables) {
const { defaultValue } = variable;
const regex = /^\$\$.*\((\d+)\)$/g;
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
if (variable.defaultValue.startsWith('$$generate_password')) {
variable.value = generatePassword({ length });
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid();
} else {
variable.value = variable.defaultValue || '';
}
}
}
for (const variable of template.variables) {
if (variable.id.startsWith('$$secret_')) {
const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSecret.create({
data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
})
}
if (variable.id.startsWith('$$secret_')) {
const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSecret.create({
data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
})
}
}
if (variable.id.startsWith('$$config_')) {
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
})
}
if (variable.id.startsWith('$$config_')) {
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
})
}
}
}
}

View File

@ -11,7 +11,7 @@ import { promises as dns } from 'dns';
import { PrismaClient } from '@prisma/client';
import os from 'os';
import sshConfig from 'ssh-config';
import jsonwebtoken from 'jsonwebtoken';
import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs';
import { saveBuildLog } from './buildPacks/common';
@ -722,6 +722,11 @@ export async function listSettings(): Promise<any> {
return settings;
}
export function generateToken() {
return jsonwebtoken.sign({
nbf: Math.floor(Date.now() / 1000) - 30,
}, process.env['COOLIFY_SECRET_KEY'])
}
export function generatePassword({
length = 24,
symbols = false,
@ -1614,7 +1619,7 @@ export function persistentVolumes(id, persistentStorage, config) {
for (const [key, value] of Object.entries(config)) {
if (value.volumes) {
for (const volume of value.volumes) {
if (!volume.startsWith('/var/run/docker.sock')) {
if (!volume.startsWith('/')) {
volumeSet.add(volume);
}
}

View File

@ -103,9 +103,19 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
}
}
}
let port = null
if (template.services[s].ports?.length > 0) {
port = template.services[s].ports[0]
let ports = []
if (template.services[s].proxy?.length > 0) {
for (const proxy of template.services[s].proxy) {
if (proxy.hostPort) {
ports.push(`${proxy.hostPort}:${proxy.port}`)
}
}
} else {
if (template.services[s].ports?.length === 1) {
for (const port of template.services[s].ports) {
ports.push(`${exposePort}:${exposePort}`)
}
}
}
let image = template.services[s].image
if (arm && template.services[s].imageArm) {
@ -118,7 +128,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
entrypoint: template.services[s]?.entrypoint,
image,
expose: template.services[s].ports,
...(exposePort && port ? { ports: [`${exposePort}:${port}`] } : {}),
ports,
volumes: Array.from(volumes),
environment: newEnvironments,
depends_on: template.services[s]?.depends_on,
@ -128,7 +138,6 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
labels: makeLabelForServices(type),
...defaultComposeConfiguration(network),
}
// Generate files for builds
if (template.services[s]?.files?.length > 0) {
if (!config[s].build) {
@ -182,7 +191,6 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
`docker container ls -a --filter 'name=${id}-' --format {{.ID}}|xargs -r -n 1 docker container rm -f`
});
} catch (error) { }
}
return {}
} catch ({ status, message }) {

View File

@ -4,7 +4,7 @@ import yaml from 'js-yaml';
import bcrypt from 'bcryptjs';
import cuid from 'cuid';
import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort, listSettings } from '../../../../lib/common';
import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort, listSettings, generateToken } from '../../../../lib/common';
import { day } from '../../../../lib/dayjs';
import { checkContainer, } from '../../../../lib/docker';
import { removeService } from '../../../../lib/services/common';
@ -159,13 +159,17 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
files: value?.files,
environment: [],
fqdns: [],
hostPorts: [],
proxy: {}
}
if (value.environment?.length > 0) {
for (const env of value.environment) {
let [envKey, ...envValue] = env.split('=')
envValue = envValue.join("=")
const variable = foundTemplate.variables.find(v => v.name === envKey) || foundTemplate.variables.find(v => v.id === envValue)
let variable = null
if (foundTemplate?.variables) {
variable = foundTemplate?.variables.find(v => v.name === envKey) || foundTemplate?.variables.find(v => v.id === envValue)
}
if (variable) {
const id = variable.id.replaceAll('$$', '')
const label = variable?.label
@ -191,7 +195,7 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
if (value?.proxy && value.proxy.length > 0) {
for (const proxyValue of value.proxy) {
if (proxyValue.domain) {
const variable = foundTemplate.variables.find(v => v.id === proxyValue.domain)
const variable = foundTemplate?.variables.find(v => v.id === proxyValue.domain)
if (variable) {
const { id, name, label, description, defaultValue, required = false } = variable
const found = await prisma.serviceSetting.findFirst({ where: { serviceId: service.id, variableName: proxyValue.domain } })
@ -199,7 +203,16 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
{ id, name, value: found?.value || '', label, description, defaultValue, required }
)
}
}
if (proxyValue.hostPort) {
const variable = foundTemplate?.variables.find(v => v.id === proxyValue.hostPort)
if (variable) {
const { id, name, label, description, defaultValue, required = false } = variable
const found = await prisma.serviceSetting.findFirst({ where: { serviceId: service.id, variableName: proxyValue.hostPort } })
parsedTemplate[realKey].hostPorts.push(
{ id, name, value: found?.value || '', label, description, defaultValue, required }
)
}
}
}
}
@ -225,6 +238,8 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
const regex = new RegExp(`\\$\\$config_${variableName.replace('$$config_', '')}`, 'gi')
if (value === '$$generate_fqdn') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn || '')
} else if (value === '$$generate_fqdn_slash') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn + '/')
} else if (value === '$$generate_domain') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, getDomain(service.fqdn))
} else if (service.destinationDocker?.network && value === '$$generate_network') {
@ -297,42 +312,46 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
let foundTemplate = templates.find(t => fixType(t.type) === fixType(type))
if (foundTemplate) {
foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id))
if (foundTemplate.variables.length > 0) {
if (foundTemplate.variables) {
if (foundTemplate.variables.length > 0) {
for (const variable of foundTemplate.variables) {
const { defaultValue } = variable;
const regex = /^\$\$.*\((\d+)\)$/g;
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
if (variable.defaultValue.startsWith('$$generate_password')) {
variable.value = generatePassword({ length });
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid();
} else if (variable.defaultValue.startsWith('$$generate_token')) {
variable.value = generateToken()
} else {
variable.value = variable.defaultValue || '';
}
const foundVariableSomewhereElse = foundTemplate.variables.find(v => v.defaultValue.includes(variable.id))
if (foundVariableSomewhereElse) {
foundVariableSomewhereElse.value = foundVariableSomewhereElse.value.replaceAll(variable.id, variable.value)
}
}
}
for (const variable of foundTemplate.variables) {
const { defaultValue } = variable;
const regex = /^\$\$.*\((\d+)\)$/g;
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
if (variable.defaultValue.startsWith('$$generate_password')) {
variable.value = generatePassword({ length });
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid();
} else {
variable.value = variable.defaultValue || '';
}
const foundVariableSomewhereElse = foundTemplate.variables.find(v => v.defaultValue.includes(variable.id))
if (foundVariableSomewhereElse) {
foundVariableSomewhereElse.value = foundVariableSomewhereElse.value.replaceAll(variable.id, variable.value)
}
}
}
for (const variable of foundTemplate.variables) {
if (variable.id.startsWith('$$secret_')) {
const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSecret.create({
data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
})
}
if (variable.id.startsWith('$$secret_')) {
const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSecret.create({
data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
})
}
}
if (variable.id.startsWith('$$config_')) {
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
})
}
if (variable.id.startsWith('$$config_')) {
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) {
await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
})
}
}
}
}
@ -538,7 +557,7 @@ export async function saveService(request: FastifyRequest<SaveService>, reply: F
}
if (isNew) {
if (!variableName) {
variableName = foundTemplate.variables.find(v => v.name === name).id
variableName = foundTemplate?.variables.find(v => v.name === name).id
}
await prisma.serviceSetting.create({ data: { name, value, variableName, service: { connect: { id } } } })
}

View File

@ -395,8 +395,8 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
}
found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id));
for (const oneService of Object.keys(found.services)) {
const isProxyConfiguration = found.services[oneService].proxy;
if (isProxyConfiguration) {
const isDomainConfiguration = found.services[oneService].proxy && found.services[oneService].proxy.filter(p => p.domain);
if (isDomainConfiguration.length > 0) {
const { proxy } = found.services[oneService];
for (let configuration of proxy) {
if (configuration.domain) {
@ -431,20 +431,24 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
}
} else {
if (found.services[oneService].ports && found.services[oneService].ports.length > 0) {
let port = found.services[oneService].ports[0]
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
if (foundPortVariable) {
port = foundPortVariable.value
for (let [index, port] of found.services[oneService].ports.entries()) {
if (port == 22) continue;
if (index === 0) {
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
if (foundPortVariable) {
port = foundPortVariable.value
}
}
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const pathPrefix = '/'
const isCustomSSL = false
const serviceId = `${oneService}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
}
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const pathPrefix = '/'
const isCustomSSL = false
const serviceId = `${oneService}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -291,7 +291,7 @@
/>
</div>
{:else}
<input class="w-full border-red-500" disabled placeholder="Error getting tags...">
<input class="w-full border-red-500" disabled placeholder="Error getting tags..." />
{/if}
</div>
@ -444,6 +444,16 @@
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.defaultValue === '$$generate_fqdn_slash'}
<CopyPasswordField
disabled
readonly
name={variable.name}
id={variable.name}
value={service.fqdn + '/' || ''}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.defaultValue === '$$generate_domain'}
<CopyPasswordField
disabled