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 - 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 documentation: https://www.keycloak.org/documentation
type: keycloak type: keycloak
name: Keycloak name: Keycloak
@@ -12,7 +80,7 @@
services: services:
$$id: $$id:
name: Keycloak name: Keycloak
command: start --db=postgres command: start --db=postgres --features=token-exchange --import-realm
depends_on: depends_on:
- $$id-postgresql - $$id-postgresql
image: "quay.io/keycloak/keycloak:$$core_version" image: "quay.io/keycloak/keycloak:$$core_version"
@@ -29,7 +97,7 @@
- KC_DB_USERNAME=$$config_postgres_user - KC_DB_USERNAME=$$config_postgres_user
- KC_DB_URL=$$secret_keycloak_database_url - KC_DB_URL=$$secret_keycloak_database_url
ports: ports:
- '8080' - "8080"
$$id-postgresql: $$id-postgresql:
name: PostgreSQL name: PostgreSQL
depends_on: [] depends_on: []
@@ -138,7 +206,7 @@
- id: $$config_port - id: $$config_port
name: PORT name: PORT
label: Port label: Port
defaultValue: '2333' defaultValue: "2333"
required: true required: true
- id: $$secret_password - id: $$secret_password
name: PASSWORD name: PASSWORD
@@ -2243,6 +2311,7 @@
description: "" description: ""
showOnConfiguration: true showOnConfiguration: true
- templateVersion: 1.0.0 - templateVersion: 1.0.0
ignore: true
defaultVersion: latest defaultVersion: latest
documentation: https://docs.ghost.org documentation: https://docs.ghost.org
type: ghost-mariadb type: ghost-mariadb

View File

@@ -1,5 +1,5 @@
import cuid from "cuid"; 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"; import { getTemplates } from "./lib/services";
export async function migrateApplicationPersistentStorage() { export async function migrateApplicationPersistentStorage() {
@@ -83,7 +83,7 @@ export async function migrateServicesToNewTemplate() {
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
if (template.variables) {
if (template.variables.length > 0) { if (template.variables.length > 0) {
for (const variable of template.variables) { for (const variable of template.variables) {
const { defaultValue } = variable; const { defaultValue } = variable;
@@ -95,6 +95,8 @@ export async function migrateServicesToNewTemplate() {
variable.value = generatePassword({ length, isHex: true }); variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) { } else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid(); variable.value = cuid();
} else if (variable.defaultValue.startsWith('$$generate_token')) {
variable.value = generateToken()
} else { } else {
variable.value = variable.defaultValue || ''; variable.value = variable.defaultValue || '';
} }
@@ -119,6 +121,7 @@ export async function migrateServicesToNewTemplate() {
} }
} }
} }
}
for (const s of Object.keys(template.services)) { for (const s of Object.keys(template.services)) {
if (service.type === 'plausibleanalytics') { if (service.type === 'plausibleanalytics') {
continue; continue;

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import yaml from 'js-yaml';
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import cuid from 'cuid'; 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 { day } from '../../../../lib/dayjs';
import { checkContainer, } from '../../../../lib/docker'; import { checkContainer, } from '../../../../lib/docker';
import { removeService } from '../../../../lib/services/common'; import { removeService } from '../../../../lib/services/common';
@@ -159,13 +159,17 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
files: value?.files, files: value?.files,
environment: [], environment: [],
fqdns: [], fqdns: [],
hostPorts: [],
proxy: {} proxy: {}
} }
if (value.environment?.length > 0) { if (value.environment?.length > 0) {
for (const env of value.environment) { for (const env of value.environment) {
let [envKey, ...envValue] = env.split('=') let [envKey, ...envValue] = env.split('=')
envValue = envValue.join("=") 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) { if (variable) {
const id = variable.id.replaceAll('$$', '') const id = variable.id.replaceAll('$$', '')
const label = variable?.label const label = variable?.label
@@ -191,7 +195,7 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
if (value?.proxy && value.proxy.length > 0) { if (value?.proxy && value.proxy.length > 0) {
for (const proxyValue of value.proxy) { for (const proxyValue of value.proxy) {
if (proxyValue.domain) { 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) { if (variable) {
const { id, name, label, description, defaultValue, required = false } = variable const { id, name, label, description, defaultValue, required = false } = variable
const found = await prisma.serviceSetting.findFirst({ where: { serviceId: service.id, variableName: proxyValue.domain } }) 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 } { 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') const regex = new RegExp(`\\$\\$config_${variableName.replace('$$config_', '')}`, 'gi')
if (value === '$$generate_fqdn') { if (value === '$$generate_fqdn') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn || '') strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn || '')
} else if (value === '$$generate_fqdn_slash') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn + '/')
} else if (value === '$$generate_domain') { } else if (value === '$$generate_domain') {
strParsedTemplate = strParsedTemplate.replaceAll(regex, getDomain(service.fqdn)) strParsedTemplate = strParsedTemplate.replaceAll(regex, getDomain(service.fqdn))
} else if (service.destinationDocker?.network && value === '$$generate_network') { } else if (service.destinationDocker?.network && value === '$$generate_network') {
@@ -297,6 +312,7 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
let foundTemplate = templates.find(t => fixType(t.type) === fixType(type)) let foundTemplate = templates.find(t => fixType(t.type) === fixType(type))
if (foundTemplate) { if (foundTemplate) {
foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id)) foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id))
if (foundTemplate.variables) {
if (foundTemplate.variables.length > 0) { if (foundTemplate.variables.length > 0) {
for (const variable of foundTemplate.variables) { for (const variable of foundTemplate.variables) {
const { defaultValue } = variable; const { defaultValue } = variable;
@@ -308,6 +324,8 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
variable.value = generatePassword({ length, isHex: true }); variable.value = generatePassword({ length, isHex: true });
} else if (variable.defaultValue.startsWith('$$generate_username')) { } else if (variable.defaultValue.startsWith('$$generate_username')) {
variable.value = cuid(); variable.value = cuid();
} else if (variable.defaultValue.startsWith('$$generate_token')) {
variable.value = generateToken()
} else { } else {
variable.value = variable.defaultValue || ''; variable.value = variable.defaultValue || '';
} }
@@ -336,6 +354,7 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
} }
} }
} }
}
for (const service of Object.keys(foundTemplate.services)) { for (const service of Object.keys(foundTemplate.services)) {
if (foundTemplate.services[service].volumes) { if (foundTemplate.services[service].volumes) {
for (const volume of foundTemplate.services[service].volumes) { for (const volume of foundTemplate.services[service].volumes) {
@@ -538,7 +557,7 @@ export async function saveService(request: FastifyRequest<SaveService>, reply: F
} }
if (isNew) { if (isNew) {
if (!variableName) { 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 } } } }) 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)); 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 isProxyConfiguration = found.services[oneService].proxy; const isDomainConfiguration = found.services[oneService].proxy && found.services[oneService].proxy.filter(p => p.domain);
if (isProxyConfiguration) { 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) {
@@ -431,11 +431,14 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
} }
} else { } else {
if (found.services[oneService].ports && found.services[oneService].ports.length > 0) { if (found.services[oneService].ports && found.services[oneService].ports.length > 0) {
let port = found.services[oneService].ports[0] 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') 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://');
@@ -448,6 +451,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
} }
} }
} }
}
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }

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> </div>
{:else} {: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} {/if}
</div> </div>
@@ -444,6 +444,16 @@
placeholder={variable.placeholder} placeholder={variable.placeholder}
required={variable?.required} 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'} {:else if variable.defaultValue === '$$generate_domain'}
<CopyPasswordField <CopyPasswordField
disabled disabled