Compare commits

...

9 Commits

Author SHA1 Message Date
Andras Bacsai
7b8f81f1b2 Merge pull request #713 from coollabsio/next
fixes
2022-11-08 11:21:41 +01:00
Andras Bacsai
62e60fc7ab fix: simplify webhooks 2022-11-08 11:15:56 +01:00
Andras Bacsai
ccd3d4aded fix: preview webhooks 2022-11-08 10:40:11 +01:00
Andras Bacsai
f0cf155b5c Merge pull request #712 from coollabsio/next
fix: migrate template
2022-11-08 10:33:20 +01:00
Andras Bacsai
b66f67d889 fix: migrate template 2022-11-08 10:19:02 +01:00
Andras Bacsai
e5dc07bde1 Merge pull request #711 from coollabsio/next
Quick fixes
2022-11-08 10:09:16 +01:00
Andras Bacsai
a955eb0fec fix: coolify instance proxy 2022-11-08 10:08:47 +01:00
Andras Bacsai
c070af9681 Merge pull request #710 from coollabsio/next
v3.11.1
2022-11-08 09:55:03 +01:00
Andras Bacsai
c15e060ef2 fix: appwrite webhook 2022-11-08 09:54:25 +01:00
8 changed files with 121 additions and 130 deletions

View File

@@ -1,5 +1,6 @@
.DS_Store .DS_Store
node_modules node_modules
.pnpm-store
build build
.svelte-kit .svelte-kit
package package
@@ -9,4 +10,8 @@ package
dist dist
client client
apps/api/db/*.db apps/api/db/*.db
local-serve local-serve
apps/api/db/migration.db-journal
apps/api/core*
logs
others/certificates

View File

@@ -6,34 +6,6 @@ on:
- next - next
jobs: jobs:
arm64:
runs-on: [self-hosted, arm64]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: "next"
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get current package version
uses: martinbeentjes/npm-get-version-action@v1.2.3
id: package-version
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
platforms: linux/arm64
push: true
tags: coollabsio/coolify:next-arm64
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-arm64
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-arm64,mode=max
amd64: amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -64,7 +36,7 @@ jobs:
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max
merge-manifest: merge-manifest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [arm64, amd64] needs: [amd64]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -79,7 +51,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create & publish manifest - name: Create & publish manifest
run: | run: |
docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64
docker manifest push coollabsio/coolify:next docker manifest push coollabsio/coolify:next
- uses: sarisia/actions-status-discord@v1 - uses: sarisia/actions-status-discord@v1
if: always() if: always()

View File

@@ -786,6 +786,11 @@
label: MariaDB | _APP_DB_USER label: MariaDB | _APP_DB_USER
defaultValue: user defaultValue: user
description: MariaDB server user name. description: MariaDB server user name.
- id: "$$secret__app_db_root_pass"
name: MARIADB_ROOT_PASSWORD
label: MariaDB | MARIADB_ROOT_PASSWORD
defaultValue: "$$generate_hex(16)"
description: MariaDB server root user password.
- id: "$$secret__app_db_pass" - id: "$$secret__app_db_pass"
name: _APP_DB_PASS name: _APP_DB_PASS
label: MariaDB | _APP_DB_PASS label: MariaDB | _APP_DB_PASS

View File

@@ -30,85 +30,92 @@ export async function migrateServicesToNewTemplate() {
} }
}) })
for (const service of services) { for (const service of services) {
const { id } = service try {
if (!service.type) { const { id } = service
continue; if (!service.type) {
} continue;
let template = templates.find(t => fixType(t.type) === fixType(service.type)); }
if (template) { let template = templates.find(t => fixType(t.type) === fixType(service.type));
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id)) if (template) {
if (service.type === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service, template) template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id))
if (service.type === 'fider' && service.fider) await fider(service, template) if (service.type === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service, template)
if (service.type === 'minio' && service.minio) await minio(service, template) if (service.type === 'fider' && service.fider) await fider(service, template)
if (service.type === 'vscodeserver' && service.vscodeserver) await vscodeserver(service, template) if (service.type === 'minio' && service.minio) await minio(service, template)
if (service.type === 'wordpress' && service.wordpress) await wordpress(service, template) if (service.type === 'vscodeserver' && service.vscodeserver) await vscodeserver(service, template)
if (service.type === 'ghost' && service.ghost) await ghost(service, template) if (service.type === 'wordpress' && service.wordpress) await wordpress(service, template)
if (service.type === 'meilisearch' && service.meiliSearch) await meilisearch(service, template) if (service.type === 'ghost' && service.ghost) await ghost(service, template)
if (service.type === 'umami' && service.umami) await umami(service, template) if (service.type === 'meilisearch' && service.meiliSearch) await meilisearch(service, template)
if (service.type === 'hasura' && service.hasura) await hasura(service, template) if (service.type === 'umami' && service.umami) await umami(service, template)
if (service.type === 'glitchTip' && service.glitchTip) await glitchtip(service, template) if (service.type === 'hasura' && service.hasura) await hasura(service, template)
if (service.type === 'searxng' && service.searxng) await searxng(service, template) if (service.type === 'glitchTip' && service.glitchTip) await glitchtip(service, template)
if (service.type === 'weblate' && service.weblate) await weblate(service, template) if (service.type === 'searxng' && service.searxng) await searxng(service, template)
if (service.type === 'appwrite' && service.appwrite) await appwrite(service, template) if (service.type === 'weblate' && service.weblate) await weblate(service, template)
if (service.type === 'appwrite' && service.appwrite) await appwrite(service, template)
await createVolumes(service, template); try {
await createVolumes(service, template);
} catch (error) {
console.log(error)
}
if (template.variables.length > 0) { 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 {
variable.value = variable.defaultValue || '';
}
}
}
for (const variable of template.variables) { for (const variable of template.variables) {
const { defaultValue } = variable; if (variable.id.startsWith('$$secret_')) {
const regex = /^\$\$.*\((\d+)\)$/g; const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
const length = Number(regex.exec(defaultValue)?.[1]) || undefined if (!found) {
if (variable.defaultValue.startsWith('$$generate_password')) { await prisma.serviceSecret.create({
variable.value = generatePassword({ length }); data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
} 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('$$config_')) { if (variable.id.startsWith('$$config_')) {
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } }) const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
if (!found) { if (!found) {
await prisma.serviceSetting.create({ await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } } data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
}) })
}
} }
} }
} 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; }
} if (template.services[s].volumes) {
if (template.services[s].volumes) { for (const volume of template.services[s].volumes) {
for (const volume of template.services[s].volumes) { const [volumeName, path] = volume.split(':')
const [volumeName, path] = volume.split(':') if (!volumeName.startsWith('/')) {
if (!volumeName.startsWith('/')) { const found = await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: id } })
const found = await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: id } }) if (!found) {
if (!found) { await prisma.servicePersistentStorage.create({
await prisma.servicePersistentStorage.create({ data: { volumeName, path, containerId: s, predefined: true, service: { connect: { id } } }
data: { volumeName, path, containerId: s, predefined: true, service: { connect: { id } } } });
}); }
} }
} }
} }
} }
await prisma.service.update({ where: { id }, data: { templateVersion: template.templateVersion } })
} }
await prisma.service.update({ where: { id }, data: { templateVersion: template.templateVersion } }) } catch (error) {
console.log(error)
} }
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)

View File

@@ -17,7 +17,7 @@ import { day } from './dayjs';
import { saveBuildLog } from './buildPacks/common'; import { saveBuildLog } from './buildPacks/common';
import { scheduler } from './scheduler'; import { scheduler } from './scheduler';
export const version = '3.11.0'; export const version = '3.11.1';
export const isDev = process.env.NODE_ENV === 'development'; export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr'; const algorithm = 'aes-256-ctr';

View File

@@ -3,9 +3,9 @@ import { errorHandler, getDomain, isDev, prisma, executeDockerCmd, fixType } fro
import { getTemplates } from "../../../lib/services"; import { getTemplates } from "../../../lib/services";
import { OnlyId } from "../../../types"; import { OnlyId } from "../../../types";
function generateServices(id, containerId, port) { function generateServices(serviceId, containerId, port) {
return { return {
[`${id}-${port || 'default'}`]: { [serviceId]: {
loadbalancer: { loadbalancer: {
servers: [ servers: [
{ {
@@ -16,18 +16,18 @@ function generateServices(id, containerId, port) {
} }
} }
} }
function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, isDualCerts, isCustomSSL) { function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, isDualCerts, isCustomSSL) {
let http: any = { let http: any = {
entrypoints: ['web'], entrypoints: ['web'],
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`, rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
service: `${id}`, 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: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
service: `${id}`, service: `${serviceId}`,
priority: 2, priority: 2,
tls: { tls: {
certresolver: 'letsencrypt' certresolver: 'letsencrypt'
@@ -37,14 +37,14 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
let httpWWW: any = { let httpWWW: any = {
entrypoints: ['web'], entrypoints: ['web'],
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`, rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
service: `${id}`, 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: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
service: `${id}`, service: `${serviceId}`,
priority: 2, priority: 2,
tls: { tls: {
certresolver: 'letsencrypt' certresolver: 'letsencrypt'
@@ -129,10 +129,10 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
} }
} }
return { return {
[id]: { ...http }, [serviceId]: { ...http },
[`${id}-secure`]: { ...https }, [`${serviceId}-secure`]: { ...https },
[`${id}-www`]: { ...httpWWW }, [`${serviceId}-www`]: { ...httpWWW },
[`${id}-secure-www`]: { ...httpsWWW }, [`${serviceId}-secure-www`]: { ...httpsWWW },
} }
} }
export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote: boolean = false) { export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote: boolean = false) {
@@ -310,9 +310,10 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const pathPrefix = '/' const pathPrefix = '/'
const isCustomSSL = false; const isCustomSSL = false;
const dualCerts = false; const dualCerts = false;
const serviceId = `${id}-${port || 'default'}-${pathPrefix}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${id}-${port || 'default'}`, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(id, containerId, port) } traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, containerId, port) }
} }
} }
} }
@@ -328,9 +329,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
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'}-${pathPrefix}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${id}-${port || 'default'}`, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(id, id, port) } traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
if (previews) { if (previews) {
const { stdout } = await executeDockerCmd({ dockerId, command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` }) const { stdout } = await executeDockerCmd({ dockerId, command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` })
const containers = stdout const containers = stdout
@@ -343,9 +344,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const previewDomain = `${container.split('-')[1]}.${domain}`; const previewDomain = `${container.split('-')[1]}.${domain}`;
const nakedDomain = previewDomain.replace(/^www\./, ''); const nakedDomain = previewDomain.replace(/^www\./, '');
const pathPrefix = '/' const pathPrefix = '/'
const serviceId = `${container}-${port || 'default'}-${pathPrefix}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${container}-${port || 'default'}`, previewDomain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, previewDomain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(container, container, port) } traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
} }
} }
} }
@@ -412,7 +413,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
let port, pathPrefix, customDomain; let port, pathPrefix, customDomain;
if (configuration) { if (configuration) {
port = configuration?.port; port = configuration?.port;
pathPrefix = configuration?.pathPrefix || null; pathPrefix = configuration?.pathPrefix || '/';
customDomain = configuration?.domain customDomain = configuration?.domain
} }
if (customDomain) { if (customDomain) {
@@ -425,8 +426,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
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;
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${id}-${port || 'default'}`, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } const serviceId = `${oneService}-${port || 'default'}-${pathPrefix}`
traefik.http.services = { ...traefik.http.services, ...generateServices(id, id, port) } traefik.http.routers = { ...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) {
@@ -441,9 +443,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
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'}-${pathPrefix}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${id}-${port || 'default'}`, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(id, id, port) } traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
} }
} }
} }
@@ -466,9 +468,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const port = 3000 const port = 3000
const pathPrefix = '/' const pathPrefix = '/'
const isCustomSSL = false const isCustomSSL = false
const serviceId = `${id}-${port || 'default'}-${pathPrefix}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(`${id}-${port || 'default'}`, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) } traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(id, container, port) } traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.11.0", "version": "3.11.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": "github:coollabsio/coolify", "repository": "github:coollabsio/coolify",
"scripts": { "scripts": {
@@ -25,7 +25,7 @@
"build:api": "NODE_ENV=production pnpm run --filter api build", "build:api": "NODE_ENV=production pnpm run --filter api build",
"build:ui": "NODE_ENV=production pnpm run --filter ui build", "build:ui": "NODE_ENV=production pnpm run --filter ui build",
"dockerlogin": "echo $DOCKER_PASS | docker login --username=$DOCKER_USER --password-stdin", "dockerlogin": "echo $DOCKER_PASS | docker login --username=$DOCKER_USER --password-stdin",
"release:staging:amd": "cross-var docker buildx build --platform linux/amd64 -t coollabsio/coolify:$npm_package_version --push .", "release:staging:amd": "cross-var docker buildx build --platform linux/amd64 -t coollabsio/coolify:next --push .",
"release:local": "rm -fr ./local-serve && mkdir ./local-serve && pnpm build && cp -Rp apps/api/build/* ./local-serve && cp -Rp apps/ui/build/ ./local-serve/public && cp -Rp apps/api/prisma/ ./local-serve/prisma && cp -Rp apps/api/package.json ./local-serve && env | grep '^COOLIFY_' > ./local-serve/.env && cd ./local-serve && pnpm install . && pnpm start" "release:local": "rm -fr ./local-serve && mkdir ./local-serve && pnpm build && cp -Rp apps/api/build/* ./local-serve && cp -Rp apps/ui/build/ ./local-serve/public && cp -Rp apps/api/prisma/ ./local-serve/prisma && cp -Rp apps/api/package.json ./local-serve && env | grep '^COOLIFY_' > ./local-serve/.env && cd ./local-serve && pnpm install . && pnpm start"
}, },
"devDependencies": { "devDependencies": {