mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-19 12:33:11 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33b853b981 | ||
|
|
e6063fb93a | ||
|
|
f30f23af59 | ||
|
|
d4798a3b22 | ||
|
|
f3beb5d8db | ||
|
|
e86b916415 | ||
|
|
e14cc6f2f0 | ||
|
|
8c1eb94401 | ||
|
|
29fa421945 | ||
|
|
7cfe98d988 | ||
|
|
e2314c350b | ||
|
|
3713b33578 | ||
|
|
e007a773fd | ||
|
|
e2821118eb | ||
|
|
4c8e73ac86 | ||
|
|
cb980fb814 | ||
|
|
41c84e3642 | ||
|
|
2bad98424f | ||
|
|
bc6b1e2dea | ||
|
|
911c15d1be | ||
|
|
f79d570870 | ||
|
|
7fffa9fba5 | ||
|
|
cbd634fb99 | ||
|
|
7ae7436d4f | ||
|
|
641bada100 | ||
|
|
3416d8d88e | ||
|
|
0bb503368b | ||
|
|
ac3a77c3c7 | ||
|
|
79b4178d76 | ||
|
|
42a61296d7 |
3
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
3
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
@@ -2,9 +2,6 @@ name: 🐞 Bug report
|
|||||||
description: Create a bug report to help us improve coolify
|
description: Create a bug report to help us improve coolify
|
||||||
title: "[Bug]: "
|
title: "[Bug]: "
|
||||||
labels: [Bug]
|
labels: [Bug]
|
||||||
assignees:
|
|
||||||
- andrasbacsai
|
|
||||||
- vasani-arpit
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ name: 🛠️ Feature request
|
|||||||
description: Suggest an idea to improve coolify
|
description: Suggest an idea to improve coolify
|
||||||
title: '[Feature]: '
|
title: '[Feature]: '
|
||||||
labels: [Enhancement]
|
labels: [Enhancement]
|
||||||
assignees:
|
|
||||||
- andrasbacsai
|
|
||||||
- vasani-arpit
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
name: Production Release to DockerHub
|
name: Production Release to DockerHub
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
push:
|
||||||
types: [released]
|
branches:
|
||||||
|
- "this-branch-does-not-exists"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
arm64:
|
arm64:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: Staging Release to DockerHub
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "next"
|
- "this-branch-does-not-exists"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
arm64:
|
arm64:
|
||||||
|
|||||||
14
.github/workflows/staging-release.yml
vendored
14
.github/workflows/staging-release.yml
vendored
@@ -1,23 +1,24 @@
|
|||||||
name: Staging Release to ghcr.io
|
name: Staging Release to ghcr.io
|
||||||
|
concurrency:
|
||||||
|
group: staging_environment
|
||||||
|
cancel-in-progress: true
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- "main"
|
- "main"
|
||||||
|
- "v4"
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
IMAGE_NAME: "coollabsio/coolify"
|
IMAGE_NAME: "coollabsio/coolify"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: "next"
|
ref: "next"
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
@@ -40,14 +41,13 @@ jobs:
|
|||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on:
|
||||||
|
group: aarch-runners
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: "next"
|
ref: "next"
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Deploy your resource to:
|
|||||||
|
|
||||||
- Mastodon: [@andrasbacsai@fosstodon.org](https://fosstodon.org/@andrasbacsai)
|
- Mastodon: [@andrasbacsai@fosstodon.org](https://fosstodon.org/@andrasbacsai)
|
||||||
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
||||||
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
- Twitter: [@andrasbacsai](https://twitter.com/heyandras)
|
||||||
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
||||||
- Discord: [Invitation](https://coollabs.io/discord)
|
- Discord: [Invitation](https://coollabs.io/discord)
|
||||||
|
|
||||||
|
|||||||
@@ -268,6 +268,13 @@
|
|||||||
- DISABLE_REGISTRATION=$$config_disable_registration
|
- DISABLE_REGISTRATION=$$config_disable_registration
|
||||||
- DATABASE_URL=$$secret_database_url
|
- DATABASE_URL=$$secret_database_url
|
||||||
- CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url
|
- CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url
|
||||||
|
- MAILER_EMAIL=$$config_mailer_email
|
||||||
|
- SMTP_HOST_ADDR=$$config_smtp_host_addr
|
||||||
|
- SMTP_HOST_PORT=$$config_smtp_host_port
|
||||||
|
- SMTP_USER_NAME=$$config_smtp_user_name
|
||||||
|
- SMTP_USER_PWD=$$config_smtp_user_pwd
|
||||||
|
- SMTP_HOST_SSL_ENABLED=$$config_smtp_host_ssl_enabled
|
||||||
|
- SMTP_HOST_RETRIES=$$config_smtp_host_retries
|
||||||
ports:
|
ports:
|
||||||
- "8000"
|
- "8000"
|
||||||
$$id-postgresql:
|
$$id-postgresql:
|
||||||
@@ -375,6 +382,49 @@
|
|||||||
label: PostgreSQL Database
|
label: PostgreSQL Database
|
||||||
defaultValue: plausible
|
defaultValue: plausible
|
||||||
description: ""
|
description: ""
|
||||||
|
- id: $$config_mailer_email
|
||||||
|
name: MAILER_EMAIL
|
||||||
|
label: Mailer Email
|
||||||
|
defaultValue: hello@plausible.local
|
||||||
|
description: >-
|
||||||
|
The email id to use for as from address of all communications from Plausible.
|
||||||
|
- id: $$config_smtp_host_addr
|
||||||
|
name: SMTP_HOST_ADDR
|
||||||
|
label: SMTP Host Address
|
||||||
|
defaultValue: localhost
|
||||||
|
description: >-
|
||||||
|
The host address of your smtp server.
|
||||||
|
- id: $$config_smtp_host_port
|
||||||
|
name: SMTP_HOST_PORT
|
||||||
|
label: SMTP Port
|
||||||
|
defaultValue: "25"
|
||||||
|
description: >-
|
||||||
|
The port of your smtp server.
|
||||||
|
- id: $$config_smtp_user_name
|
||||||
|
name: SMTP_USER_NAME
|
||||||
|
label: SMTP Username
|
||||||
|
defaultValue: ""
|
||||||
|
description: >-
|
||||||
|
The username/email in case SMTP auth is enabled.
|
||||||
|
- id: $$config_smtp_user_pwd
|
||||||
|
name: SMTP_USER_PWD
|
||||||
|
label: SMTP Password
|
||||||
|
defaultValue: ""
|
||||||
|
description: >-
|
||||||
|
The password in case SMTP auth is enabled.
|
||||||
|
showOnConfiguration: true
|
||||||
|
- id: $$config_smtp_host_ssl_enabled
|
||||||
|
name: SMTP_HOST_SSL_ENABLED
|
||||||
|
label: SMTP SSL
|
||||||
|
defaultValue: "false"
|
||||||
|
description: >-
|
||||||
|
If SSL is enabled for SMTP connection.
|
||||||
|
- id: $$config_smtp_host_retries
|
||||||
|
name: SMTP_HOST_RETRIES
|
||||||
|
label: SMTP Retries
|
||||||
|
defaultValue: "2"
|
||||||
|
description: >-
|
||||||
|
Number of retries to make until mailer gives up.
|
||||||
- id: $$config_scriptName
|
- id: $$config_scriptName
|
||||||
name: SCRIPT_NAME
|
name: SCRIPT_NAME
|
||||||
label: Custom Script Name
|
label: Custom Script Name
|
||||||
@@ -414,6 +464,7 @@
|
|||||||
proxy:
|
proxy:
|
||||||
- port: "22"
|
- port: "22"
|
||||||
hostPort: $$config_hostport_ssh
|
hostPort: $$config_hostport_ssh
|
||||||
|
- port: "3000"
|
||||||
variables:
|
variables:
|
||||||
- id: $$config_hostport_ssh
|
- id: $$config_hostport_ssh
|
||||||
name: SSH_PORT
|
name: SSH_PORT
|
||||||
@@ -3388,6 +3439,13 @@
|
|||||||
- DISABLE_REGISTRATION=$$config_disable_registration
|
- DISABLE_REGISTRATION=$$config_disable_registration
|
||||||
- DATABASE_URL=$$secret_database_url
|
- DATABASE_URL=$$secret_database_url
|
||||||
- CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url
|
- CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url
|
||||||
|
- MAILER_EMAIL=$$config_mailer_email
|
||||||
|
- SMTP_HOST_ADDR=$$config_smtp_host_addr
|
||||||
|
- SMTP_HOST_PORT=$$config_smtp_host_port
|
||||||
|
- SMTP_USER_NAME=$$config_smtp_user_name
|
||||||
|
- SMTP_USER_PWD=$$config_smtp_user_pwd
|
||||||
|
- SMTP_HOST_SSL_ENABLED=$$config_smtp_host_ssl_enabled
|
||||||
|
- SMTP_HOST_RETRIES=$$config_smtp_host_retries
|
||||||
ports:
|
ports:
|
||||||
- "8000"
|
- "8000"
|
||||||
$$id-postgresql:
|
$$id-postgresql:
|
||||||
@@ -3495,6 +3553,49 @@
|
|||||||
label: PostgreSQL Database
|
label: PostgreSQL Database
|
||||||
defaultValue: plausible
|
defaultValue: plausible
|
||||||
description: ""
|
description: ""
|
||||||
|
- id: $$config_mailer_email
|
||||||
|
name: MAILER_EMAIL
|
||||||
|
label: Mailer Email
|
||||||
|
defaultValue: hello@plausible.local
|
||||||
|
description: >-
|
||||||
|
The email id to use for as from address of all communications from Plausible.
|
||||||
|
- id: $$config_smtp_host_addr
|
||||||
|
name: SMTP_HOST_ADDR
|
||||||
|
label: SMTP Host Address
|
||||||
|
defaultValue: localhost
|
||||||
|
description: >-
|
||||||
|
The host address of your smtp server.
|
||||||
|
- id: $$config_smtp_host_port
|
||||||
|
name: SMTP_HOST_PORT
|
||||||
|
label: SMTP Port
|
||||||
|
defaultValue: "25"
|
||||||
|
description: >-
|
||||||
|
The port of your smtp server.
|
||||||
|
- id: $$config_smtp_user_name
|
||||||
|
name: SMTP_USER_NAME
|
||||||
|
label: SMTP Username
|
||||||
|
defaultValue: ""
|
||||||
|
description: >-
|
||||||
|
The username/email in case SMTP auth is enabled.
|
||||||
|
- id: $$config_smtp_user_pwd
|
||||||
|
name: SMTP_USER_PWD
|
||||||
|
label: SMTP Password
|
||||||
|
defaultValue: ""
|
||||||
|
description: >-
|
||||||
|
The password in case SMTP auth is enabled.
|
||||||
|
showOnConfiguration: true
|
||||||
|
- id: $$config_smtp_host_ssl_enabled
|
||||||
|
name: SMTP_HOST_SSL_ENABLED
|
||||||
|
label: SMTP SSL
|
||||||
|
defaultValue: "false"
|
||||||
|
description: >-
|
||||||
|
If SSL is enabled for SMTP connection.
|
||||||
|
- id: $$config_smtp_host_retries
|
||||||
|
name: SMTP_HOST_RETRIES
|
||||||
|
label: SMTP Retries
|
||||||
|
defaultValue: "2"
|
||||||
|
description: >-
|
||||||
|
Number of retries to make until mailer gives up.
|
||||||
- id: $$config_scriptName
|
- id: $$config_scriptName
|
||||||
name: SCRIPT_NAME
|
name: SCRIPT_NAME
|
||||||
label: Custom Script Name
|
label: Custom Script Name
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ async function initServer() {
|
|||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
try {
|
try {
|
||||||
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
|
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
|
||||||
await fs.rm('/tmp/build-sources', { recursive: true, force: true });
|
if (!isDev) await fs.rm('/tmp/build-sources', { recursive: true, force: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
@@ -604,53 +604,54 @@ async function cleanupStorage() {
|
|||||||
if (!destination.remoteVerified) continue;
|
if (!destination.remoteVerified) continue;
|
||||||
enginesDone.add(destination.remoteIpAddress);
|
enginesDone.add(destination.remoteIpAddress);
|
||||||
}
|
}
|
||||||
let lowDiskSpace = false;
|
|
||||||
try {
|
|
||||||
let stdout = null;
|
|
||||||
if (!isDev) {
|
|
||||||
const output = await executeCommand({
|
|
||||||
dockerId: destination.id,
|
|
||||||
command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
|
|
||||||
shell: true
|
|
||||||
});
|
|
||||||
stdout = output.stdout;
|
|
||||||
} else {
|
|
||||||
const output = await executeCommand({
|
|
||||||
command: `df -kPT /`
|
|
||||||
});
|
|
||||||
stdout = output.stdout;
|
|
||||||
}
|
|
||||||
let lines = stdout.trim().split('\n');
|
|
||||||
let header = lines[0];
|
|
||||||
let regex =
|
|
||||||
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
|
||||||
const boundaries = [];
|
|
||||||
let match;
|
|
||||||
|
|
||||||
while ((match = regex.exec(header))) {
|
|
||||||
boundaries.push(match[0].length);
|
|
||||||
}
|
|
||||||
|
|
||||||
boundaries[boundaries.length - 1] = -1;
|
|
||||||
const data = lines.slice(1).map((line) => {
|
|
||||||
const cl = boundaries.map((boundary) => {
|
|
||||||
const column = boundary > 0 ? line.slice(0, boundary) : line;
|
|
||||||
line = line.slice(boundary);
|
|
||||||
return column.trim();
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
capacity: Number.parseInt(cl[5], 10) / 100
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if (data.length > 0) {
|
|
||||||
const { capacity } = data[0];
|
|
||||||
if (capacity > 0.8) {
|
|
||||||
lowDiskSpace = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {}
|
|
||||||
if (lowDiskSpace) {
|
|
||||||
await cleanupDockerStorage(destination.id);
|
await cleanupDockerStorage(destination.id);
|
||||||
}
|
// let lowDiskSpace = false;
|
||||||
|
// try {
|
||||||
|
// let stdout = null;
|
||||||
|
// if (!isDev) {
|
||||||
|
// const output = await executeCommand({
|
||||||
|
// dockerId: destination.id,
|
||||||
|
// command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
|
||||||
|
// shell: true
|
||||||
|
// });
|
||||||
|
// stdout = output.stdout;
|
||||||
|
// } else {
|
||||||
|
// const output = await executeCommand({
|
||||||
|
// command: `df -kPT /`
|
||||||
|
// });
|
||||||
|
// stdout = output.stdout;
|
||||||
|
// }
|
||||||
|
// let lines = stdout.trim().split('\n');
|
||||||
|
// let header = lines[0];
|
||||||
|
// let regex =
|
||||||
|
// /^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
||||||
|
// const boundaries = [];
|
||||||
|
// let match;
|
||||||
|
|
||||||
|
// while ((match = regex.exec(header))) {
|
||||||
|
// boundaries.push(match[0].length);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// boundaries[boundaries.length - 1] = -1;
|
||||||
|
// const data = lines.slice(1).map((line) => {
|
||||||
|
// const cl = boundaries.map((boundary) => {
|
||||||
|
// const column = boundary > 0 ? line.slice(0, boundary) : line;
|
||||||
|
// line = line.slice(boundary);
|
||||||
|
// return column.trim();
|
||||||
|
// });
|
||||||
|
// return {
|
||||||
|
// capacity: Number.parseInt(cl[5], 10) / 100
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// if (data.length > 0) {
|
||||||
|
// const { capacity } = data[0];
|
||||||
|
// if (capacity > 0.8) {
|
||||||
|
// lowDiskSpace = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (error) {}
|
||||||
|
// if (lowDiskSpace) {
|
||||||
|
// await cleanupDockerStorage(destination.id);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,15 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
teams: true
|
teams: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!application) {
|
||||||
|
await prisma.build.update({
|
||||||
|
where: { id: queueBuild.id },
|
||||||
|
data: {
|
||||||
|
status: 'failed'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw new Error('Application not found');
|
||||||
|
}
|
||||||
let {
|
let {
|
||||||
id: buildId,
|
id: buildId,
|
||||||
type,
|
type,
|
||||||
@@ -111,7 +119,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
.replace('-app', '')}:${storage.path}`;
|
.replace('-app', '')}:${storage.path}`;
|
||||||
}
|
}
|
||||||
if (storage.hostPath) {
|
if (storage.hostPath) {
|
||||||
return `${storage.hostPath}:${storage.path}`
|
return `${storage.hostPath}:${storage.path}`;
|
||||||
}
|
}
|
||||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||||
}) || [];
|
}) || [];
|
||||||
@@ -163,11 +171,18 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
port: exposePort ? `${exposePort}:${port}` : port
|
port: exposePort ? `${exposePort}:${port}` : port
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const composeVolumes = volumes.filter(v => {
|
const composeVolumes = volumes
|
||||||
if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) {
|
.filter((v) => {
|
||||||
|
if (
|
||||||
|
!v.startsWith('.') &&
|
||||||
|
!v.startsWith('..') &&
|
||||||
|
!v.startsWith('/') &&
|
||||||
|
!v.startsWith('~')
|
||||||
|
) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}).map((volume) => {
|
})
|
||||||
|
.map((volume) => {
|
||||||
return {
|
return {
|
||||||
[`${volume.split(':')[0]}`]: {
|
[`${volume.split(':')[0]}`]: {
|
||||||
name: volume.split(':')[0]
|
name: volume.split(':')[0]
|
||||||
@@ -240,7 +255,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
applicationId: application.id
|
applicationId: application.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await fs.rm(workdir, { recursive: true, force: true });
|
if (!isDev) await fs.rm(workdir, { recursive: true, force: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -263,7 +278,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await fs.rm(workdir, { recursive: true, force: true });
|
if (!isDev) await fs.rm(workdir, { recursive: true, force: true });
|
||||||
await prisma.build.update({
|
await prisma.build.update({
|
||||||
where: { id: buildId },
|
where: { id: buildId },
|
||||||
data: { status: 'success' }
|
data: { status: 'success' }
|
||||||
@@ -389,7 +404,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
.replace('-app', '')}:${storage.path}`;
|
.replace('-app', '')}:${storage.path}`;
|
||||||
}
|
}
|
||||||
if (storage.hostPath) {
|
if (storage.hostPath) {
|
||||||
return `${storage.hostPath}:${storage.path}`
|
return `${storage.hostPath}:${storage.path}`;
|
||||||
}
|
}
|
||||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||||
}) || [];
|
}) || [];
|
||||||
@@ -504,7 +519,8 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
try {
|
try {
|
||||||
await executeCommand({
|
await executeCommand({
|
||||||
dockerId: destinationDocker.id,
|
dockerId: destinationDocker.id,
|
||||||
command: `docker ${location ? `--config ${location}` : ''
|
command: `docker ${
|
||||||
|
location ? `--config ${location}` : ''
|
||||||
} pull ${imageName}:${customTag}`
|
} pull ${imageName}:${customTag}`
|
||||||
});
|
});
|
||||||
imageFoundRemotely = true;
|
imageFoundRemotely = true;
|
||||||
@@ -668,7 +684,8 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
try {
|
try {
|
||||||
const { stdout: containers } = await executeCommand({
|
const { stdout: containers } = await executeCommand({
|
||||||
dockerId: destinationDockerId,
|
dockerId: destinationDockerId,
|
||||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${pullmergeRequestId ? imageId : applicationId
|
command: `docker ps -a --filter 'label=com.docker.compose.service=${
|
||||||
|
pullmergeRequestId ? imageId : applicationId
|
||||||
}' --format {{.ID}}`
|
}' --format {{.ID}}`
|
||||||
});
|
});
|
||||||
if (containers) {
|
if (containers) {
|
||||||
@@ -701,11 +718,18 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const composeVolumes = volumes.filter(v => {
|
const composeVolumes = volumes
|
||||||
if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) {
|
.filter((v) => {
|
||||||
|
if (
|
||||||
|
!v.startsWith('.') &&
|
||||||
|
!v.startsWith('..') &&
|
||||||
|
!v.startsWith('/') &&
|
||||||
|
!v.startsWith('~')
|
||||||
|
) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}).map((volume) => {
|
})
|
||||||
|
.map((volume) => {
|
||||||
return {
|
return {
|
||||||
[`${volume.split(':')[0]}`]: {
|
[`${volume.split(':')[0]}`]: {
|
||||||
name: volume.split(':')[0]
|
name: volume.split(':')[0]
|
||||||
@@ -782,7 +806,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
applicationId: application.id
|
applicationId: application.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await fs.rm(workdir, { recursive: true, force: true });
|
if (!isDev) await fs.rm(workdir, { recursive: true, force: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -803,7 +827,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await fs.rm(workdir, { recursive: true, force: true });
|
if (!isDev) await fs.rm(workdir, { recursive: true, force: true });
|
||||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,19 +86,20 @@ export async function migrateServicesToNewTemplate() {
|
|||||||
if (template.variables) {
|
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;
|
let { defaultValue } = variable;
|
||||||
|
defaultValue = defaultValue.toString();
|
||||||
const regex = /^\$\$.*\((\d+)\)$/g;
|
const regex = /^\$\$.*\((\d+)\)$/g;
|
||||||
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
|
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
|
||||||
if (variable.defaultValue.startsWith('$$generate_password')) {
|
if (defaultValue.startsWith('$$generate_password')) {
|
||||||
variable.value = generatePassword({ length });
|
variable.value = generatePassword({ length });
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
|
} else if (defaultValue.startsWith('$$generate_hex')) {
|
||||||
variable.value = generatePassword({ length, isHex: true });
|
variable.value = generatePassword({ length, isHex: true });
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_username')) {
|
} else if (defaultValue.startsWith('$$generate_username')) {
|
||||||
variable.value = cuid();
|
variable.value = cuid();
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_token')) {
|
} else if (defaultValue.startsWith('$$generate_token')) {
|
||||||
variable.value = generateToken()
|
variable.value = generateToken()
|
||||||
} else {
|
} else {
|
||||||
variable.value = variable.defaultValue || '';
|
variable.value = defaultValue || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -804,6 +804,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
}
|
}
|
||||||
Dockerfile.push(`RUN ${buildCommand}`);
|
Dockerfile.push(`RUN ${buildCommand}`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||||
await buildImage({ ...data, isCache: true });
|
await buildImage({ ...data, isCache: true });
|
||||||
}
|
}
|
||||||
@@ -821,6 +822,7 @@ export async function buildCacheImageForLaravel(data, imageForBuild) {
|
|||||||
}
|
}
|
||||||
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
||||||
Dockerfile.push(`COPY resources /app/resources`);
|
Dockerfile.push(`COPY resources /app/resources`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`RUN yarn install && yarn production`);
|
Dockerfile.push(`RUN yarn install && yarn production`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||||
await buildImage({ ...data, isCache: true });
|
await buildImage({ ...data, isCache: true });
|
||||||
@@ -842,6 +844,7 @@ export async function buildCacheImageWithCargo(data, imageForBuild) {
|
|||||||
Dockerfile.push('RUN cargo install cargo-chef');
|
Dockerfile.push('RUN cargo install cargo-chef');
|
||||||
Dockerfile.push(`COPY --from=planner-${applicationId} /app/recipe.json recipe.json`);
|
Dockerfile.push(`COPY --from=planner-${applicationId} /app/recipe.json recipe.json`);
|
||||||
Dockerfile.push('RUN cargo chef cook --release --recipe-path recipe.json');
|
Dockerfile.push('RUN cargo chef cook --release --recipe-path recipe.json');
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||||
await buildImage({ ...data, isCache: true });
|
await buildImage({ ...data, isCache: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,12 @@ export default async function (data) {
|
|||||||
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
|
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
|
||||||
value['container_name'] = `${applicationId}-${key}`;
|
value['container_name'] = `${applicationId}-${key}`;
|
||||||
|
|
||||||
|
if (value['env_file']) {
|
||||||
|
delete value['env_file'];
|
||||||
|
}
|
||||||
|
|
||||||
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment'];
|
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment'];
|
||||||
|
console.log({ key, environment });
|
||||||
if (Object.keys(environment).length > 0) {
|
if (Object.keys(environment).length > 0) {
|
||||||
environment = Object.entries(environment).map(([key, value]) => `${key}=${value}`);
|
environment = Object.entries(environment).map(([key, value]) => `${key}=${value}`);
|
||||||
}
|
}
|
||||||
@@ -60,7 +65,7 @@ export default async function (data) {
|
|||||||
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
|
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
|
||||||
let finalArgs = [...buildEnvs];
|
let finalArgs = [...buildEnvs];
|
||||||
if (Object.keys(buildArgs).length > 0) {
|
if (Object.keys(buildArgs).length > 0) {
|
||||||
for (const arg of buildArgs) {
|
for (const arg of Object.keys(buildArgs)) {
|
||||||
const [key, _] = arg.split('=');
|
const [key, _] = arg.split('=');
|
||||||
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
|
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
|
||||||
finalArgs.push(arg);
|
finalArgs.push(arg);
|
||||||
@@ -87,7 +92,10 @@ export default async function (data) {
|
|||||||
v.startsWith('~') ||
|
v.startsWith('~') ||
|
||||||
v.startsWith('$PWD')
|
v.startsWith('$PWD')
|
||||||
) {
|
) {
|
||||||
v = v.replace(/^\./, `~`).replace(/^\.\./, '~').replace(/^\$PWD/, '~');
|
v = v
|
||||||
|
.replace(/^\./, `~`)
|
||||||
|
.replace(/^\.\./, '~')
|
||||||
|
.replace(/^\$PWD/, '~');
|
||||||
} else {
|
} else {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
path = v;
|
path = v;
|
||||||
@@ -110,10 +118,11 @@ export default async function (data) {
|
|||||||
source.startsWith('~') ||
|
source.startsWith('~') ||
|
||||||
source.startsWith('$PWD')
|
source.startsWith('$PWD')
|
||||||
) {
|
) {
|
||||||
|
source = source
|
||||||
source = source.replace(/^\./, `~`).replace(/^\.\./, '~').replace(/^\$PWD/, '~');
|
.replace(/^\./, `~`)
|
||||||
console.log({source})
|
.replace(/^\.\./, '~')
|
||||||
|
.replace(/^\$PWD/, '~');
|
||||||
|
console.log({ source });
|
||||||
} else {
|
} else {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
target = source;
|
target = source;
|
||||||
@@ -125,7 +134,6 @@ export default async function (data) {
|
|||||||
|
|
||||||
return `${source}:${target}${mode ? ':' + mode : ''}`;
|
return `${source}:${target}${mode ? ':' + mode : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (volumes.length > 0) {
|
if (volumes.length > 0) {
|
||||||
@@ -136,16 +144,20 @@ export default async function (data) {
|
|||||||
if (dockerComposeConfiguration[key]?.port) {
|
if (dockerComposeConfiguration[key]?.port) {
|
||||||
value['expose'] = [dockerComposeConfiguration[key].port];
|
value['expose'] = [dockerComposeConfiguration[key].port];
|
||||||
}
|
}
|
||||||
if (value['networks']?.length > 0) {
|
|
||||||
value['networks'].forEach((network) => {
|
|
||||||
networks[network] = {
|
|
||||||
name: network
|
|
||||||
};
|
|
||||||
});
|
|
||||||
value['networks'] = [...(value['networks'] || ''), network];
|
|
||||||
} else {
|
|
||||||
value['networks'] = [network];
|
value['networks'] = [network];
|
||||||
|
if (value['build']?.network) {
|
||||||
|
delete value['build']['network'];
|
||||||
}
|
}
|
||||||
|
// if (value['networks']?.length > 0) {
|
||||||
|
// value['networks'].forEach((network) => {
|
||||||
|
// networks[network] = {
|
||||||
|
// name: network
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// value['networks'] = [...(value['networks'] || ''), network];
|
||||||
|
// } else {
|
||||||
|
// value['networks'] = [network];
|
||||||
|
// }
|
||||||
|
|
||||||
dockerComposeYaml.services[key] = {
|
dockerComposeYaml.services[key] = {
|
||||||
...dockerComposeYaml.services[key],
|
...dockerComposeYaml.services[key],
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
||||||
Dockerfile.push(`ENV NO_COLOR true`);
|
Dockerfile.push(`ENV NO_COLOR true`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD deno run ${denoOptions || ''} ${denoMainFile}`);
|
Dockerfile.push(`CMD deno run ${denoOptions || ''} ${denoMainFile}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
|||||||
if (baseImage?.includes('nginx')) {
|
if (baseImage?.includes('nginx')) {
|
||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json`
|
`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json`
|
||||||
);
|
);
|
||||||
Dockerfile.push(`COPY --chown=application:application . ./`);
|
Dockerfile.push(`COPY --chown=application:application . ./`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD ${startCommand}`);
|
Dockerfile.push(`CMD ${startCommand}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
Dockerfile.push(`RUN ${buildCommand}`);
|
Dockerfile.push(`RUN ${buildCommand}`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD ${startCommand}`);
|
Dockerfile.push(`CMD ${startCommand}`);
|
||||||
} else if (deploymentType === 'static') {
|
} else if (deploymentType === 'static') {
|
||||||
@@ -43,6 +44,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE 80`);
|
Dockerfile.push(`EXPOSE 80`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`RUN ${buildCommand}`);
|
Dockerfile.push(`RUN ${buildCommand}`);
|
||||||
}
|
}
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`CMD ${startCommand}`);
|
Dockerfile.push(`CMD ${startCommand}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
Dockerfile.push(`RUN ${buildCommand}`);
|
Dockerfile.push(`RUN ${buildCommand}`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD ${startCommand}`);
|
Dockerfile.push(`CMD ${startCommand}`);
|
||||||
} else if (deploymentType === 'static') {
|
} else if (deploymentType === 'static') {
|
||||||
@@ -43,6 +44,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE 80`);
|
Dockerfile.push(`EXPOSE 80`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
|
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
} else {
|
} else {
|
||||||
Dockerfile.push(`CMD python ${pythonModule}`);
|
Dockerfile.push(`CMD python ${pythonModule}`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (baseImage?.includes('nginx')) {
|
if (baseImage?.includes('nginx')) {
|
||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const createDockerfile = async (data, image, name): Promise<void> => {
|
|||||||
);
|
);
|
||||||
Dockerfile.push(`RUN update-ca-certificates`);
|
Dockerfile.push(`RUN update-ca-certificates`);
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/target/release/${name} ${name}`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/target/release/${name} ${name}`);
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD ["/app/${name}"]`);
|
Dockerfile.push(`CMD ["/app/${name}"]`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (baseImage?.includes('nginx')) {
|
if (baseImage?.includes('nginx')) {
|
||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (baseImage?.includes('nginx')) {
|
if (baseImage?.includes('nginx')) {
|
||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (baseImage?.includes('nginx')) {
|
if (baseImage?.includes('nginx')) {
|
||||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||||
}
|
}
|
||||||
|
Dockerfile.push('RUN rm -fr .git');
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { saveBuildLog } 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.28';
|
export const version = '3.12.32';
|
||||||
export const isDev = process.env.NODE_ENV === 'development';
|
export const isDev = process.env.NODE_ENV === 'development';
|
||||||
export const proxyPort = process.env.COOLIFY_PROXY_PORT;
|
export const proxyPort = process.env.COOLIFY_PROXY_PORT;
|
||||||
export const proxySecurePort = process.env.COOLIFY_PROXY_SECURE_PORT;
|
export const proxySecurePort = process.env.COOLIFY_PROXY_SECURE_PORT;
|
||||||
@@ -402,8 +402,8 @@ export const supportedDatabaseTypesAndVersions = [
|
|||||||
fancyName: 'MongoDB',
|
fancyName: 'MongoDB',
|
||||||
baseImage: 'bitnami/mongodb',
|
baseImage: 'bitnami/mongodb',
|
||||||
baseImageARM: 'mongo',
|
baseImageARM: 'mongo',
|
||||||
versions: ['5.0', '4.4', '4.2'],
|
versions: ['6.0', '5.0', '4.4', '4.2'],
|
||||||
versionsARM: ['5.0', '4.4', '4.2']
|
versionsARM: ['6.0', '5.0', '4.4', '4.2']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mysql',
|
name: 'mysql',
|
||||||
@@ -418,16 +418,16 @@ export const supportedDatabaseTypesAndVersions = [
|
|||||||
fancyName: 'MariaDB',
|
fancyName: 'MariaDB',
|
||||||
baseImage: 'bitnami/mariadb',
|
baseImage: 'bitnami/mariadb',
|
||||||
baseImageARM: 'mariadb',
|
baseImageARM: 'mariadb',
|
||||||
versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'],
|
versions: ['10.11', '10.10', '10.9', '10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'],
|
||||||
versionsARM: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
|
versionsARM: ['10.11', '10.10', '10.9', '10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'postgresql',
|
name: 'postgresql',
|
||||||
fancyName: 'PostgreSQL',
|
fancyName: 'PostgreSQL',
|
||||||
baseImage: 'bitnami/postgresql',
|
baseImage: 'bitnami/postgresql',
|
||||||
baseImageARM: 'postgres',
|
baseImageARM: 'postgres',
|
||||||
versions: ['14.5.0', '13.8.0', '12.12.0', '11.17.0', '10.22.0'],
|
versions: ['15.2.0', '14.7.0', '14.5.0', '13.8.0', '12.12.0', '11.17.0', '10.22.0'],
|
||||||
versionsARM: ['14.5', '13.8', '12.12', '11.17', '10.22']
|
versionsARM: ['15.2', '14.7', '14.5', '13.8', '12.12', '11.17', '10.22']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'redis',
|
name: 'redis',
|
||||||
@@ -442,14 +442,14 @@ export const supportedDatabaseTypesAndVersions = [
|
|||||||
fancyName: 'CouchDB',
|
fancyName: 'CouchDB',
|
||||||
baseImage: 'bitnami/couchdb',
|
baseImage: 'bitnami/couchdb',
|
||||||
baseImageARM: 'couchdb',
|
baseImageARM: 'couchdb',
|
||||||
versions: ['3.2.2', '3.1.2', '2.3.1'],
|
versions: ['3.3.1', '3.2.2', '3.1.2', '2.3.1'],
|
||||||
versionsARM: ['3.2.2', '3.1.2', '2.3.1']
|
versionsARM: ['3.3', '3.2.2', '3.1.2', '2.3.1']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'edgedb',
|
name: 'edgedb',
|
||||||
fancyName: 'EdgeDB',
|
fancyName: 'EdgeDB',
|
||||||
baseImage: 'edgedb/edgedb',
|
baseImage: 'edgedb/edgedb',
|
||||||
versions: ['latest', '2.1', '2.0', '1.4']
|
versions: ['latest', '2.9', '2.8', '2.7']
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -579,7 +579,8 @@ export async function executeCommand({
|
|||||||
stream = false,
|
stream = false,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId,
|
applicationId,
|
||||||
debug
|
debug,
|
||||||
|
timeout = 0
|
||||||
}: {
|
}: {
|
||||||
command: string;
|
command: string;
|
||||||
sshCommand?: boolean;
|
sshCommand?: boolean;
|
||||||
@@ -589,6 +590,7 @@ export async function executeCommand({
|
|||||||
buildId?: string;
|
buildId?: string;
|
||||||
applicationId?: string;
|
applicationId?: string;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
|
timeout?: number;
|
||||||
}): Promise<ExecaChildProcess<string>> {
|
}): Promise<ExecaChildProcess<string>> {
|
||||||
const { execa, execaCommand } = await import('execa');
|
const { execa, execaCommand } = await import('execa');
|
||||||
const { parse } = await import('shell-quote');
|
const { parse } = await import('shell-quote');
|
||||||
@@ -613,20 +615,26 @@ export async function executeCommand({
|
|||||||
}
|
}
|
||||||
if (sshCommand) {
|
if (sshCommand) {
|
||||||
if (shell) {
|
if (shell) {
|
||||||
return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`);
|
return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`, {
|
||||||
|
timeout
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs]);
|
return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs], {
|
||||||
|
timeout
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (stream) {
|
if (stream) {
|
||||||
return await new Promise(async (resolve, reject) => {
|
return await new Promise(async (resolve, reject) => {
|
||||||
let subprocess = null;
|
let subprocess = null;
|
||||||
if (shell) {
|
if (shell) {
|
||||||
subprocess = execaCommand(command, {
|
subprocess = execaCommand(command, {
|
||||||
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine }
|
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
|
||||||
|
timeout
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
subprocess = execa(dockerCommand, dockerArgs, {
|
subprocess = execa(dockerCommand, dockerArgs, {
|
||||||
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine }
|
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
|
||||||
|
timeout
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const logs = [];
|
const logs = [];
|
||||||
@@ -680,19 +688,26 @@ export async function executeCommand({
|
|||||||
} else {
|
} else {
|
||||||
if (shell) {
|
if (shell) {
|
||||||
return await execaCommand(command, {
|
return await execaCommand(command, {
|
||||||
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine }
|
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
|
||||||
|
timeout
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return await execa(dockerCommand, dockerArgs, {
|
return await execa(dockerCommand, dockerArgs, {
|
||||||
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine }
|
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
|
||||||
|
timeout
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (shell) {
|
if (shell) {
|
||||||
return execaCommand(command, { shell: true });
|
return execaCommand(command, {
|
||||||
|
shell: true,
|
||||||
|
timeout
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return await execa(dockerCommand, dockerArgs);
|
return await execa(dockerCommand, dockerArgs, {
|
||||||
|
timeout
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,7 +1028,7 @@ export function generateDatabaseConfiguration(database: any): DatabaseConfigurat
|
|||||||
ulimits: {}
|
ulimits: {}
|
||||||
};
|
};
|
||||||
if (isARM()) {
|
if (isARM()) {
|
||||||
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
|
configuration.volume = `${id}-${type}-data:/var/lib/postgresql/data`;
|
||||||
configuration.environmentVariables = {
|
configuration.environmentVariables = {
|
||||||
POSTGRES_PASSWORD: dbUserPassword,
|
POSTGRES_PASSWORD: dbUserPassword,
|
||||||
POSTGRES_USER: dbUser,
|
POSTGRES_USER: dbUser,
|
||||||
|
|||||||
@@ -640,8 +640,7 @@ export async function restartApplication(
|
|||||||
|
|
||||||
const volumes =
|
const volumes =
|
||||||
persistentStorage?.map((storage) => {
|
persistentStorage?.map((storage) => {
|
||||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : ''
|
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||||
}${storage.path}`;
|
|
||||||
}) || [];
|
}) || [];
|
||||||
const composeVolumes = volumes.map((volume) => {
|
const composeVolumes = volumes.map((volume) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
|
|||||||
databaseSecret
|
databaseSecret
|
||||||
} = database;
|
} = database;
|
||||||
const { privatePort, command, environmentVariables, image, volume, ulimits } =
|
const { privatePort, command, environmentVariables, image, volume, ulimits } =
|
||||||
generateDatabaseConfiguration(database, arch);
|
generateDatabaseConfiguration(database);
|
||||||
|
|
||||||
const network = destinationDockerId && destinationDocker.network;
|
const network = destinationDockerId && destinationDocker.network;
|
||||||
const volumeName = volume.split(':')[0];
|
const volumeName = volume.split(':')[0];
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
import type { FastifyRequest } from 'fastify';
|
import type { FastifyRequest } from 'fastify';
|
||||||
import { FastifyReply } from 'fastify';
|
import { FastifyReply } from 'fastify';
|
||||||
import sshConfig from 'ssh-config'
|
import {
|
||||||
import fs from 'fs/promises'
|
errorHandler,
|
||||||
import os from 'os';
|
executeCommand,
|
||||||
|
listSettings,
|
||||||
import { createRemoteEngineConfiguration, decrypt, errorHandler, executeCommand, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
prisma,
|
||||||
|
startTraefikProxy,
|
||||||
|
stopTraefikProxy
|
||||||
|
} from '../../../../lib/common';
|
||||||
import { checkContainer } from '../../../../lib/docker';
|
import { checkContainer } from '../../../../lib/docker';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { CheckDestination, ListDestinations, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
import type {
|
||||||
|
CheckDestination,
|
||||||
|
ListDestinations,
|
||||||
|
NewDestination,
|
||||||
|
Proxy,
|
||||||
|
SaveDestinationSettings
|
||||||
|
} from './types';
|
||||||
|
import { removeService } from '../../../../lib/services/common';
|
||||||
|
|
||||||
export async function listDestinations(request: FastifyRequest<ListDestinations>) {
|
export async function listDestinations(request: FastifyRequest<ListDestinations>) {
|
||||||
try {
|
try {
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const { onlyVerified = false } = request.query
|
const { onlyVerified = false } = request.query;
|
||||||
let destinations = []
|
let destinations = [];
|
||||||
if (teamId === '0') {
|
if (teamId === '0') {
|
||||||
destinations = await prisma.destinationDocker.findMany({ include: { teams: true } });
|
destinations = await prisma.destinationDocker.findMany({ include: { teams: true } });
|
||||||
} else {
|
} else {
|
||||||
@@ -24,13 +34,16 @@ export async function listDestinations(request: FastifyRequest<ListDestinations>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (onlyVerified) {
|
if (onlyVerified) {
|
||||||
destinations = destinations.filter(destination => destination.engine || (destination.remoteEngine && destination.remoteVerified))
|
destinations = destinations.filter(
|
||||||
|
(destination) =>
|
||||||
|
destination.engine || (destination.remoteEngine && destination.remoteVerified)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
destinations
|
destinations
|
||||||
}
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function checkDestination(request: FastifyRequest<CheckDestination>) {
|
export async function checkDestination(request: FastifyRequest<CheckDestination>) {
|
||||||
@@ -42,14 +55,14 @@ export async function checkDestination(request: FastifyRequest<CheckDestination>
|
|||||||
message: `Network already exists: ${network}`
|
message: `Network already exists: ${network}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getDestination(request: FastifyRequest<OnlyId>) {
|
export async function getDestination(request: FastifyRequest<OnlyId>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
const teamId = request.user?.teamId;
|
const teamId = request.user?.teamId;
|
||||||
const destination = await prisma.destinationDocker.findFirst({
|
const destination = await prisma.destinationDocker.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
@@ -66,20 +79,22 @@ export async function getDestination(request: FastifyRequest<OnlyId>) {
|
|||||||
return {
|
return {
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function newDestination(request: FastifyRequest<NewDestination>, reply: FastifyReply) {
|
export async function newDestination(request: FastifyRequest<NewDestination>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
|
|
||||||
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
|
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } =
|
||||||
|
request.body;
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
if (engine) {
|
if (engine) {
|
||||||
const { stdout } = await await executeCommand({ command: `docker network ls --filter 'name=^${network}$' --format '{{json .}}'` });
|
const { stdout } = await await executeCommand({
|
||||||
|
command: `docker network ls --filter 'name=^${network}$' --format '{{json .}}'`
|
||||||
|
});
|
||||||
if (stdout === '') {
|
if (stdout === '') {
|
||||||
await await executeCommand({ command: `docker network create --attachable ${network}` });
|
await await executeCommand({ command: `docker network create --attachable ${network}` });
|
||||||
}
|
}
|
||||||
@@ -90,12 +105,16 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
const destination = destinations.find((destination) => destination.network === network);
|
const destination = destinations.find((destination) => destination.network === network);
|
||||||
if (destinations.length > 0) {
|
if (destinations.length > 0) {
|
||||||
const proxyConfigured = destinations.find(
|
const proxyConfigured = destinations.find(
|
||||||
(destination) => destination.network !== network && destination.isCoolifyProxyUsed === true
|
(destination) =>
|
||||||
|
destination.network !== network && destination.isCoolifyProxyUsed === true
|
||||||
);
|
);
|
||||||
if (proxyConfigured) {
|
if (proxyConfigured) {
|
||||||
isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed;
|
isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed;
|
||||||
}
|
}
|
||||||
await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
|
await prisma.destinationDocker.updateMany({
|
||||||
|
where: { engine },
|
||||||
|
data: { isCoolifyProxyUsed }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (isCoolifyProxyUsed) {
|
if (isCoolifyProxyUsed) {
|
||||||
await startTraefikProxy(destination.id);
|
await startTraefikProxy(destination.id);
|
||||||
@@ -103,39 +122,89 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
return reply.code(201).send({ id: destination.id });
|
return reply.code(201).send({ id: destination.id });
|
||||||
} else {
|
} else {
|
||||||
const destination = await prisma.destinationDocker.create({
|
const destination = await prisma.destinationDocker.create({
|
||||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed, remoteEngine: true, remoteIpAddress, remoteUser, remotePort: Number(remotePort) }
|
data: {
|
||||||
|
name,
|
||||||
|
teams: { connect: { id: teamId } },
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine: true,
|
||||||
|
remoteIpAddress,
|
||||||
|
remoteUser,
|
||||||
|
remotePort: Number(remotePort)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return reply.code(201).send({ id: destination.id })
|
return reply.code(201).send({ id: destination.id });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
||||||
return reply.code(201).send();
|
return reply.code(201).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function forceDeleteDestination(request: FastifyRequest<OnlyId>) {
|
||||||
|
try {
|
||||||
|
const { id } = request.params;
|
||||||
|
const services = await prisma.service.findMany({ where: { destinationDockerId: id } });
|
||||||
|
for (const service of services) {
|
||||||
|
await removeService({ id: service.id });
|
||||||
|
}
|
||||||
|
const applications = await prisma.application.findMany({ where: { destinationDockerId: id } });
|
||||||
|
for (const application of applications) {
|
||||||
|
await prisma.applicationSettings.deleteMany({ where: { application: { id: application.id } } });
|
||||||
|
await prisma.buildLog.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.build.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.secret.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.previewApplication.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
}
|
||||||
|
const databases = await prisma.database.findMany({ where: { destinationDockerId: id } });
|
||||||
|
for (const database of databases) {
|
||||||
|
await prisma.databaseSettings.deleteMany({ where: { databaseId: database.id } });
|
||||||
|
await prisma.databaseSecret.deleteMany({ where: { databaseId: database.id } });
|
||||||
|
await prisma.database.delete({ where: { id: database.id } });
|
||||||
|
}
|
||||||
|
await prisma.destinationDocker.delete({ where: { id } });
|
||||||
|
return {};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function deleteDestination(request: FastifyRequest<OnlyId>) {
|
export async function deleteDestination(request: FastifyRequest<OnlyId>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
const { network, remoteVerified, engine, isCoolifyProxyUsed } = await prisma.destinationDocker.findUnique({ where: { id } });
|
const appFound = await prisma.application.findFirst({ where: { destinationDockerId: id } });
|
||||||
|
const serviceFound = await prisma.service.findFirst({ where: { destinationDockerId: id } });
|
||||||
|
const databaseFound = await prisma.database.findFirst({ where: { destinationDockerId: id } });
|
||||||
|
if (appFound || serviceFound || databaseFound) {
|
||||||
|
throw {
|
||||||
|
message: `Destination is in use.<br>Remove all applications, services and databases using this destination first.`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { network, remoteVerified, engine, isCoolifyProxyUsed } =
|
||||||
|
await prisma.destinationDocker.findUnique({ where: { id } });
|
||||||
if (isCoolifyProxyUsed) {
|
if (isCoolifyProxyUsed) {
|
||||||
if (engine || remoteVerified) {
|
if (engine || remoteVerified) {
|
||||||
const { stdout: found } = await executeCommand({
|
const { stdout: found } = await executeCommand({
|
||||||
dockerId: id,
|
dockerId: id,
|
||||||
command: `docker ps -a --filter network=${network} --filter name=coolify-proxy --format '{{.}}'`
|
command: `docker ps -a --filter network=${network} --filter name=coolify-proxy --format '{{.}}'`
|
||||||
})
|
});
|
||||||
if (found) {
|
if (found) {
|
||||||
await executeCommand({ dockerId: id, command: `docker network disconnect ${network} coolify-proxy` })
|
await executeCommand({
|
||||||
await executeCommand({ dockerId: id, command: `docker network rm ${network}` })
|
dockerId: id,
|
||||||
|
command: `docker network disconnect ${network} coolify-proxy`
|
||||||
|
});
|
||||||
|
await executeCommand({ dockerId: id, command: `docker network rm ${network}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await prisma.destinationDocker.delete({ where: { id } });
|
await prisma.destinationDocker.delete({ where: { id } });
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function saveDestinationSettings(request: FastifyRequest<SaveDestinationSettings>) {
|
export async function saveDestinationSettings(request: FastifyRequest<SaveDestinationSettings>) {
|
||||||
@@ -148,33 +217,33 @@ export async function saveDestinationSettings(request: FastifyRequest<SaveDestin
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
status: 202
|
status: 202
|
||||||
}
|
};
|
||||||
// return reply.code(201).send();
|
// return reply.code(201).send();
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function startProxy(request: FastifyRequest<Proxy>) {
|
export async function startProxy(request: FastifyRequest<Proxy>) {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
try {
|
try {
|
||||||
await startTraefikProxy(id);
|
await startTraefikProxy(id);
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
await stopTraefikProxy(id);
|
await stopTraefikProxy(id);
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function stopProxy(request: FastifyRequest<Proxy>) {
|
export async function stopProxy(request: FastifyRequest<Proxy>) {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
try {
|
try {
|
||||||
await stopTraefikProxy(id);
|
await stopTraefikProxy(id);
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function restartProxy(request: FastifyRequest<Proxy>) {
|
export async function restartProxy(request: FastifyRequest<Proxy>) {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
try {
|
try {
|
||||||
await stopTraefikProxy(id);
|
await stopTraefikProxy(id);
|
||||||
await startTraefikProxy(id);
|
await startTraefikProxy(id);
|
||||||
@@ -182,13 +251,13 @@ export async function restartProxy(request: FastifyRequest<Proxy>) {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data: { isCoolifyProxyUsed: true }
|
data: { isCoolifyProxyUsed: true }
|
||||||
});
|
});
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
await prisma.destinationDocker.update({
|
await prisma.destinationDocker.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { isCoolifyProxyUsed: false }
|
data: { isCoolifyProxyUsed: false }
|
||||||
});
|
});
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,84 +265,120 @@ export async function assignSSHKey(request: FastifyRequest) {
|
|||||||
try {
|
try {
|
||||||
const { id: sshKeyId } = request.body;
|
const { id: sshKeyId } = request.body;
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { sshKey: { connect: { id: sshKeyId } } } })
|
await prisma.destinationDocker.update({
|
||||||
return {}
|
where: { id },
|
||||||
|
data: { sshKey: { connect: { id: sshKeyId } } }
|
||||||
|
});
|
||||||
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function verifyRemoteDockerEngineFn(id: string) {
|
export async function verifyRemoteDockerEngineFn(id: string) {
|
||||||
const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst(
|
||||||
const daemonJson = `daemon-${id}.json`
|
{ where: { id } }
|
||||||
|
);
|
||||||
|
const daemonJson = `daemon-${id}.json`;
|
||||||
try {
|
try {
|
||||||
await executeCommand({ sshCommand: true, command: `docker network inspect ${network}`, dockerId: id });
|
await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
command: `docker network inspect ${network}`,
|
||||||
|
dockerId: id
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await executeCommand({ command: `docker network create --attachable ${network}`, dockerId: id });
|
await executeCommand({
|
||||||
|
command: `docker network create --attachable ${network}`,
|
||||||
|
dockerId: id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await executeCommand({ sshCommand: true, command: `docker network inspect coolify-infra`, dockerId: id });
|
await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
command: `docker network inspect coolify-infra`,
|
||||||
|
dockerId: id
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await executeCommand({ command: `docker network create --attachable coolify-infra`, dockerId: id });
|
await executeCommand({
|
||||||
|
command: `docker network create --attachable coolify-infra`,
|
||||||
|
dockerId: id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||||
let isUpdated = false;
|
let isUpdated = false;
|
||||||
let daemonJsonParsed = {
|
let daemonJsonParsed = {
|
||||||
"live-restore": true,
|
'live-restore': true,
|
||||||
"features": {
|
features: {
|
||||||
"buildkit": true
|
buildkit: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const { stdout: daemonJson } = await executeCommand({ sshCommand: true, dockerId: id, command: `cat /etc/docker/daemon.json` });
|
const { stdout: daemonJson } = await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
dockerId: id,
|
||||||
|
command: `cat /etc/docker/daemon.json`
|
||||||
|
});
|
||||||
daemonJsonParsed = JSON.parse(daemonJson);
|
daemonJsonParsed = JSON.parse(daemonJson);
|
||||||
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
||||||
isUpdated = true;
|
isUpdated = true;
|
||||||
daemonJsonParsed['live-restore'] = true
|
daemonJsonParsed['live-restore'] = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!daemonJsonParsed?.features?.buildkit) {
|
if (!daemonJsonParsed?.features?.buildkit) {
|
||||||
isUpdated = true;
|
isUpdated = true;
|
||||||
daemonJsonParsed.features = {
|
daemonJsonParsed.features = {
|
||||||
buildkit: true
|
buildkit: true
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
isUpdated = true;
|
isUpdated = true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
await executeCommand({ shell: true, command: `echo '${JSON.stringify(daemonJsonParsed, null, 2)}' > /tmp/${daemonJson}` })
|
await executeCommand({
|
||||||
await executeCommand({ dockerId: id, command: `scp /tmp/${daemonJson} ${remoteIpAddress}-remote:/etc/docker/daemon.json` });
|
shell: true,
|
||||||
await executeCommand({ command: `rm /tmp/${daemonJson}` })
|
command: `echo '${JSON.stringify(daemonJsonParsed, null, 2)}' > /tmp/${daemonJson}`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: id,
|
||||||
|
command: `scp /tmp/${daemonJson} ${remoteIpAddress}-remote:/etc/docker/daemon.json`
|
||||||
|
});
|
||||||
|
await executeCommand({ command: `rm /tmp/${daemonJson}` });
|
||||||
await executeCommand({ sshCommand: true, dockerId: id, command: `systemctl restart docker` });
|
await executeCommand({ sshCommand: true, dockerId: id, command: `systemctl restart docker` });
|
||||||
}
|
}
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Error while verifying remote docker engine')
|
console.log(error)
|
||||||
|
throw new Error('Error while verifying remote docker engine');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
export async function verifyRemoteDockerEngine(
|
||||||
|
request: FastifyRequest<OnlyId>,
|
||||||
|
reply: FastifyReply
|
||||||
|
) {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
try {
|
try {
|
||||||
await verifyRemoteDockerEngineFn(id);
|
await verifyRemoteDockerEngineFn(id);
|
||||||
return reply.code(201).send()
|
return reply.code(201).send();
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } })
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } });
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDestinationStatus(request: FastifyRequest<OnlyId>) {
|
export async function getDestinationStatus(request: FastifyRequest<OnlyId>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
const destination = await prisma.destinationDocker.findUnique({ where: { id } })
|
const destination = await prisma.destinationDocker.findUnique({ where: { id } });
|
||||||
const { found: isRunning } = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy', remove: true })
|
const { found: isRunning } = await checkContainer({
|
||||||
|
dockerId: destination.id,
|
||||||
|
container: 'coolify-proxy',
|
||||||
|
remove: true
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
isRunning
|
isRunning
|
||||||
}
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { assignSSHKey, checkDestination, deleteDestination, getDestination, getDestinationStatus, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy, verifyRemoteDockerEngine } from './handlers';
|
import { assignSSHKey, checkDestination, deleteDestination, forceDeleteDestination, getDestination, getDestinationStatus, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy, verifyRemoteDockerEngine } from './handlers';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { CheckDestination, ListDestinations, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
import type { CheckDestination, ListDestinations, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
||||||
@@ -14,6 +14,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get<OnlyId>('/:id', async (request) => await getDestination(request));
|
fastify.get<OnlyId>('/:id', async (request) => await getDestination(request));
|
||||||
fastify.post<NewDestination>('/:id', async (request, reply) => await newDestination(request, reply));
|
fastify.post<NewDestination>('/:id', async (request, reply) => await newDestination(request, reply));
|
||||||
fastify.delete<OnlyId>('/:id', async (request) => await deleteDestination(request));
|
fastify.delete<OnlyId>('/:id', async (request) => await deleteDestination(request));
|
||||||
|
fastify.delete<OnlyId>('/:id/force', async (request) => await forceDeleteDestination(request));
|
||||||
fastify.get<OnlyId>('/:id/status', async (request) => await getDestinationStatus(request));
|
fastify.get<OnlyId>('/:id/status', async (request) => await getDestinationStatus(request));
|
||||||
|
|
||||||
fastify.post<SaveDestinationSettings>('/:id/settings', async (request) => await saveDestinationSettings(request));
|
fastify.post<SaveDestinationSettings>('/:id/settings', async (request) => await saveDestinationSettings(request));
|
||||||
|
|||||||
@@ -412,22 +412,23 @@ export async function saveServiceType(
|
|||||||
if (foundTemplate.variables) {
|
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;
|
let { defaultValue } = variable;
|
||||||
|
defaultValue = defaultValue.toString();
|
||||||
const regex = /^\$\$.*\((\d+)\)$/g;
|
const regex = /^\$\$.*\((\d+)\)$/g;
|
||||||
const length = Number(regex.exec(defaultValue)?.[1]) || undefined;
|
const length = Number(regex.exec(defaultValue)?.[1]) || undefined;
|
||||||
if (variable.defaultValue.startsWith('$$generate_password')) {
|
if (defaultValue.startsWith('$$generate_password')) {
|
||||||
variable.value = generatePassword({ length });
|
variable.value = generatePassword({ length });
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
|
} else if (defaultValue.startsWith('$$generate_hex')) {
|
||||||
variable.value = generatePassword({ length, isHex: true });
|
variable.value = generatePassword({ length, isHex: true });
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_username')) {
|
} else if (defaultValue.startsWith('$$generate_username')) {
|
||||||
variable.value = cuid();
|
variable.value = cuid();
|
||||||
} else if (variable.defaultValue.startsWith('$$generate_token')) {
|
} else if (defaultValue.startsWith('$$generate_token')) {
|
||||||
variable.value = generateToken();
|
variable.value = generateToken();
|
||||||
} else {
|
} else {
|
||||||
variable.value = variable.defaultValue || '';
|
variable.value = defaultValue || '';
|
||||||
}
|
}
|
||||||
const foundVariableSomewhereElse = foundTemplate.variables.find((v) =>
|
const foundVariableSomewhereElse = foundTemplate.variables.find((v) =>
|
||||||
v.defaultValue.includes(variable.id)
|
v.defaultValue.toString().includes(variable.id)
|
||||||
);
|
);
|
||||||
if (foundVariableSomewhereElse) {
|
if (foundVariableSomewhereElse) {
|
||||||
foundVariableSomewhereElse.value = foundVariableSomewhereElse.value.replaceAll(
|
foundVariableSomewhereElse.value = foundVariableSomewhereElse.value.replaceAll(
|
||||||
@@ -746,7 +747,10 @@ export async function saveService(request: FastifyRequest<SaveService>, reply: F
|
|||||||
let { id: settingId, name, value, changed = false, isNew = false, variableName } = setting;
|
let { id: settingId, name, value, changed = false, isNew = false, variableName } = setting;
|
||||||
if (value) {
|
if (value) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
await prisma.serviceSetting.update({ where: { id: settingId }, data: { value: value.replace(/\n/, "\\n") } });
|
await prisma.serviceSetting.update({
|
||||||
|
where: { id: settingId },
|
||||||
|
data: { value: value.replace(/\n/, '\\n') }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
if (!variableName) {
|
if (!variableName) {
|
||||||
@@ -1104,11 +1108,14 @@ export async function activateWordpressFtp(
|
|||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
const volumes = [
|
const volumes = [
|
||||||
`${id}-wordpress-data:/home/${ftpUser}/wordpress`,
|
`${id}-wordpress-data:/home/${ftpUser}/wordpress`,
|
||||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
`${
|
||||||
|
isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
||||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
`${
|
||||||
|
isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
||||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
`${
|
||||||
|
isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import type { FastifyRequest } from 'fastify';
|
import type { FastifyRequest } from 'fastify';
|
||||||
import { FastifyReply } from 'fastify';
|
|
||||||
import { decrypt, encrypt, errorHandler, prisma } from '../../../../lib/common';
|
import { decrypt, encrypt, errorHandler, prisma } from '../../../../lib/common';
|
||||||
import { OnlyId } from '../../../../types';
|
import { OnlyId } from '../../../../types';
|
||||||
import { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types';
|
import { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types';
|
||||||
@@ -9,34 +8,39 @@ export async function listSources(request: FastifyRequest) {
|
|||||||
try {
|
try {
|
||||||
const teamId = request.user?.teamId;
|
const teamId = request.user?.teamId;
|
||||||
const sources = await prisma.gitSource.findMany({
|
const sources = await prisma.gitSource.findMany({
|
||||||
where: { OR: [{ teams: { some: { id: teamId === "0" ? undefined : teamId } } }, { isSystemWide: true }] },
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
{ isSystemWide: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
include: { teams: true, githubApp: true, gitlabApp: true }
|
include: { teams: true, githubApp: true, gitlabApp: true }
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
sources
|
sources
|
||||||
}
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function saveSource(request, reply) {
|
export async function saveSource(request, reply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
let { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = request.body
|
let { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = request.body;
|
||||||
if (customPort) customPort = Number(customPort)
|
if (customPort) customPort = Number(customPort);
|
||||||
await prisma.gitSource.update({
|
await prisma.gitSource.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide }
|
data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide }
|
||||||
});
|
});
|
||||||
return reply.code(201).send()
|
return reply.code(201).send();
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getSource(request: FastifyRequest<OnlyId>) {
|
export async function getSource(request: FastifyRequest<OnlyId>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
const { teamId } = request.user
|
const { teamId } = request.user;
|
||||||
const settings = await prisma.setting.findFirst({});
|
const settings = await prisma.setting.findFirst({});
|
||||||
|
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
@@ -48,40 +52,54 @@ export async function getSource(request: FastifyRequest<OnlyId>) {
|
|||||||
apiUrl: null,
|
apiUrl: null,
|
||||||
organization: null,
|
organization: null,
|
||||||
customPort: 22,
|
customPort: 22,
|
||||||
customUser: 'git',
|
customUser: 'git'
|
||||||
},
|
},
|
||||||
settings
|
settings
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = await prisma.gitSource.findFirst({
|
const source = await prisma.gitSource.findFirst({
|
||||||
where: { id, OR: [{ teams: { some: { id: teamId === "0" ? undefined : teamId } } }, { isSystemWide: true }] },
|
where: {
|
||||||
|
id,
|
||||||
|
OR: [
|
||||||
|
{ teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
{ isSystemWide: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
include: { githubApp: true, gitlabApp: true }
|
include: { githubApp: true, gitlabApp: true }
|
||||||
});
|
});
|
||||||
if (!source) {
|
if (!source) {
|
||||||
throw { status: 404, message: 'Source not found.' }
|
throw { status: 404, message: 'Source not found.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source?.githubApp?.clientSecret)
|
if (source?.githubApp?.clientSecret)
|
||||||
source.githubApp.clientSecret = decrypt(source.githubApp.clientSecret);
|
source.githubApp.clientSecret = decrypt(source.githubApp.clientSecret);
|
||||||
if (source?.githubApp?.webhookSecret)
|
if (source?.githubApp?.webhookSecret)
|
||||||
source.githubApp.webhookSecret = decrypt(source.githubApp.webhookSecret);
|
source.githubApp.webhookSecret = decrypt(source.githubApp.webhookSecret);
|
||||||
if (source?.githubApp?.privateKey) source.githubApp.privateKey = decrypt(source.githubApp.privateKey);
|
if (source?.githubApp?.privateKey)
|
||||||
if (source?.gitlabApp?.appSecret) source.gitlabApp.appSecret = decrypt(source.gitlabApp.appSecret);
|
source.githubApp.privateKey = decrypt(source.githubApp.privateKey);
|
||||||
|
if (source?.gitlabApp?.appSecret)
|
||||||
|
source.gitlabApp.appSecret = decrypt(source.gitlabApp.appSecret);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
source,
|
source,
|
||||||
settings
|
settings
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteSource(request) {
|
export async function deleteSource(request) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
|
const gitAppFound = await prisma.application.findFirst({ where: { gitSourceId: id } });
|
||||||
|
if (gitAppFound) {
|
||||||
|
throw {
|
||||||
|
status: 400,
|
||||||
|
message: 'This source is used by an application. Please remove the application first.'
|
||||||
|
};
|
||||||
|
}
|
||||||
const source = await prisma.gitSource.delete({
|
const source = await prisma.gitSource.delete({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: { githubApp: true, gitlabApp: true }
|
include: { githubApp: true, gitlabApp: true }
|
||||||
@@ -92,22 +110,21 @@ export async function deleteSource(request) {
|
|||||||
if (source.gitlabAppId) {
|
if (source.gitlabAppId) {
|
||||||
await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } });
|
await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } });
|
||||||
}
|
}
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
export async function saveGitHubSource(request: FastifyRequest<SaveGitHubSource>) {
|
export async function saveGitHubSource(request: FastifyRequest<SaveGitHubSource>) {
|
||||||
try {
|
try {
|
||||||
const { teamId } = request.user
|
const { teamId } = request.user;
|
||||||
|
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
let { name, htmlUrl, apiUrl, organization, customPort, isSystemWide } = request.body
|
let { name, htmlUrl, apiUrl, organization, customPort, isSystemWide } = request.body;
|
||||||
|
|
||||||
if (customPort) customPort = Number(customPort)
|
if (customPort) customPort = Number(customPort);
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
const newId = cuid()
|
const newId = cuid();
|
||||||
await prisma.gitSource.create({
|
await prisma.gitSource.create({
|
||||||
data: {
|
data: {
|
||||||
id: newId,
|
id: newId,
|
||||||
@@ -123,27 +140,48 @@ export async function saveGitHubSource(request: FastifyRequest<SaveGitHubSource>
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
id: newId
|
id: newId
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
throw { status: 500, message: 'Wrong request.' };
|
||||||
throw { status: 500, message: 'Wrong request.' }
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>) {
|
export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params;
|
||||||
const { teamId } = request.user
|
const { teamId } = request.user;
|
||||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort, customUser } =
|
let {
|
||||||
request.body
|
type,
|
||||||
|
name,
|
||||||
|
htmlUrl,
|
||||||
|
apiUrl,
|
||||||
|
oauthId,
|
||||||
|
appId,
|
||||||
|
appSecret,
|
||||||
|
groupName,
|
||||||
|
customPort,
|
||||||
|
customUser
|
||||||
|
} = request.body;
|
||||||
|
|
||||||
if (oauthId) oauthId = Number(oauthId);
|
if (oauthId) oauthId = Number(oauthId);
|
||||||
if (customPort) customPort = Number(customPort)
|
if (customPort) customPort = Number(customPort);
|
||||||
const encryptedAppSecret = encrypt(appSecret);
|
const encryptedAppSecret = encrypt(appSecret);
|
||||||
|
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
const newId = cuid()
|
const newId = cuid();
|
||||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, customUser, teams: { connect: { id: teamId } } } });
|
await prisma.gitSource.create({
|
||||||
|
data: {
|
||||||
|
id: newId,
|
||||||
|
type,
|
||||||
|
apiUrl,
|
||||||
|
htmlUrl,
|
||||||
|
name,
|
||||||
|
customPort,
|
||||||
|
customUser,
|
||||||
|
teams: { connect: { id: teamId } }
|
||||||
|
}
|
||||||
|
});
|
||||||
await prisma.gitlabApp.create({
|
await prisma.gitlabApp.create({
|
||||||
data: {
|
data: {
|
||||||
teams: { connect: { id: teamId } },
|
teams: { connect: { id: teamId } },
|
||||||
@@ -157,35 +195,37 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
|||||||
return {
|
return {
|
||||||
status: 201,
|
status: 201,
|
||||||
id: newId
|
id: newId
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort, customUser } });
|
await prisma.gitSource.update({
|
||||||
|
where: { id },
|
||||||
|
data: { type, apiUrl, htmlUrl, name, customPort, customUser }
|
||||||
|
});
|
||||||
await prisma.gitlabApp.update({
|
await prisma.gitlabApp.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
appId,
|
appId,
|
||||||
oauthId,
|
oauthId,
|
||||||
groupName,
|
groupName,
|
||||||
appSecret: encryptedAppSecret,
|
appSecret: encryptedAppSecret
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkGitLabOAuthID(request: FastifyRequest<CheckGitLabOAuthId>) {
|
export async function checkGitLabOAuthID(request: FastifyRequest<CheckGitLabOAuthId>) {
|
||||||
try {
|
try {
|
||||||
const { oauthId } = request.body
|
const { oauthId } = request.body;
|
||||||
const found = await prisma.gitlabApp.findFirst({ where: { oauthId: Number(oauthId) } });
|
const found = await prisma.gitlabApp.findFirst({ where: { oauthId: Number(oauthId) } });
|
||||||
if (found) {
|
if (found) {
|
||||||
throw { status: 500, message: 'OAuthID already configured in Coolify.' }
|
throw { status: 500, message: 'OAuthID already configured in Coolify.' };
|
||||||
}
|
}
|
||||||
return {}
|
return {};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,6 +543,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
const template: any = await parseAndFindServiceTemplates(service, null, true);
|
const template: any = await parseAndFindServiceTemplates(service, null, true);
|
||||||
const { proxy } = template.services[oneService] || found.services[oneService];
|
const { proxy } = template.services[oneService] || found.services[oneService];
|
||||||
for (let configuration of proxy) {
|
for (let configuration of proxy) {
|
||||||
|
if (configuration.hostPort) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (configuration.domain) {
|
if (configuration.domain) {
|
||||||
const setting = serviceSetting.find(
|
const setting = serviceSetting.find(
|
||||||
(a) => a.variableName === configuration.domain
|
(a) => a.variableName === configuration.domain
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -51,16 +51,22 @@
|
|||||||
async function loadLogs() {
|
async function loadLogs() {
|
||||||
if (logsLoading) return;
|
if (logsLoading) return;
|
||||||
try {
|
try {
|
||||||
const newLogs: any = await get(
|
const since = lastLog?.split(' ')[0] || 0;
|
||||||
`/applications/${id}/logs/${selectedService}?since=${lastLog?.split(' ')[0] || 0}`
|
const newLogs: any = await get(`/applications/${id}/logs/${selectedService}?since=${since}`);
|
||||||
);
|
|
||||||
if (newLogs.noContainer) {
|
if (newLogs.noContainer) {
|
||||||
noContainer = true;
|
noContainer = true;
|
||||||
} else {
|
} else {
|
||||||
noContainer = false;
|
noContainer = false;
|
||||||
}
|
}
|
||||||
if (newLogs?.logs && newLogs.logs[newLogs.logs.length - 1] !== logs[logs.length - 1]) {
|
if (newLogs?.logs && newLogs.logs[newLogs.logs.length - 1] !== logs[logs.length - 1]) {
|
||||||
|
if (since === 0) {
|
||||||
logs = logs.concat(newLogs.logs);
|
logs = logs.concat(newLogs.logs);
|
||||||
|
} else {
|
||||||
|
const newParsedLogs = newLogs.logs.filter((log: any) => {
|
||||||
|
return log !== logs[logs.length - 1];
|
||||||
|
});
|
||||||
|
logs = logs.concat(newParsedLogs);
|
||||||
|
}
|
||||||
lastLog = newLogs.logs[newLogs.logs.length - 1];
|
lastLog = newLogs.logs[newLogs.logs.length - 1];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -75,6 +75,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function forceDeleteDestination(destination: any) {
|
||||||
|
let sure = confirm($t('application.confirm_to_delete', { name: destination.name }));
|
||||||
|
if (sure) {
|
||||||
|
sure = confirm(
|
||||||
|
'Are you REALLY sure? This will delete all resources associated with this destination, but not on the destination (server) itself. You will have manually delete everything on the server afterwards.'
|
||||||
|
);
|
||||||
|
if (sure) {
|
||||||
|
sure = confirm('REALLY?');
|
||||||
|
if (sure) {
|
||||||
|
try {
|
||||||
|
await del(`/destinations/${destination.id}/force`, { id: destination.id });
|
||||||
|
return await goto('/', { replaceState: true });
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
function deletable() {
|
function deletable() {
|
||||||
if (!isDestinationDeletable) {
|
if (!isDestinationDeletable) {
|
||||||
return 'Please delete all resources before deleting this.';
|
return 'Please delete all resources before deleting this.';
|
||||||
@@ -88,7 +107,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $page.params.id !== 'new'}
|
{#if $page.params.id !== 'new'}
|
||||||
<nav class="header lg:flex-row flex-col-reverse">
|
<nav class="header lg:flex-row flex-col-reverse gap-2">
|
||||||
<div class="flex flex-row space-x-2 font-bold pt-10 lg:pt-0">
|
<div class="flex flex-row space-x-2 font-bold pt-10 lg:pt-0">
|
||||||
<div class="flex flex-col items-center justify-center title">
|
<div class="flex flex-col items-center justify-center title">
|
||||||
{#if $page.url.pathname === `/destinations/${$page.params.id}`}
|
{#if $page.url.pathname === `/destinations/${$page.params.id}`}
|
||||||
@@ -111,6 +130,16 @@
|
|||||||
>
|
>
|
||||||
<Tooltip triggeredBy="#delete">{deletable()}</Tooltip>
|
<Tooltip triggeredBy="#delete">{deletable()}</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-row flex-wrap justify-center lg:justify-start lg:py-0 items-center">
|
||||||
|
<button
|
||||||
|
id="forceDelete"
|
||||||
|
on:click={() => forceDeleteDestination(destination)}
|
||||||
|
type="submit"
|
||||||
|
disabled={!$appSession.isAdmin && isDestinationDeletable}
|
||||||
|
class="icons bg-transparent text-sm text-red-500"><DeleteIcon /></button
|
||||||
|
>
|
||||||
|
<Tooltip triggeredBy="#forceDelete">Force Delete</Tooltip>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{/if}
|
{/if}
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let customVersion: string;
|
||||||
$: isDisabled =
|
$: isDisabled =
|
||||||
!$appSession.isAdmin ||
|
!$appSession.isAdmin ||
|
||||||
$status.service.overallStatus === 'degraded' ||
|
$status.service.overallStatus === 'degraded' ||
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
setLocation(service);
|
setLocation(service);
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices(service);
|
$isDeploymentEnabled = checkIfDeploymentEnabledServices(service);
|
||||||
|
customVersion = null;
|
||||||
return addToast({
|
return addToast({
|
||||||
message: 'Configuration saved.',
|
message: 'Configuration saved.',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
@@ -196,26 +198,12 @@
|
|||||||
async function selectTag(event: any) {
|
async function selectTag(event: any) {
|
||||||
service.version = event.detail.value;
|
service.version = event.detail.value;
|
||||||
}
|
}
|
||||||
|
async function setCustomVersion() {
|
||||||
|
service.version = customVersion;
|
||||||
|
}
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
// if (service.type === 'wordpress') {
|
|
||||||
// service.wordpress.mysqlDatabase = 'db';
|
|
||||||
// }
|
|
||||||
// if (service.type === 'plausibleanalytics') {
|
|
||||||
// service.plausibleAnalytics.email = 'noreply@demo.com';
|
|
||||||
// service.plausibleAnalytics.username = 'admin';
|
|
||||||
// }
|
|
||||||
// if (service.type === 'minio') {
|
|
||||||
// service.minio.apiFqdn = `http://${cuid()}.demo.coolify.io`;
|
|
||||||
// }
|
|
||||||
// if (service.type === 'ghost') {
|
|
||||||
// service.ghost.mariadbDatabase = 'db';
|
|
||||||
// }
|
|
||||||
// if (service.type === 'fider') {
|
|
||||||
// service.fider.emailNoreply = 'noreply@demo.com';
|
|
||||||
// }
|
|
||||||
// await handleSubmit();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -289,6 +277,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="version">Version / Tag</label>
|
<label for="version">Version / Tag</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
{#if tags.tags?.length > 0}
|
{#if tags.tags?.length > 0}
|
||||||
<div class="custom-select-wrapper w-full">
|
<div class="custom-select-wrapper w-full">
|
||||||
<Select
|
<Select
|
||||||
@@ -306,6 +295,8 @@
|
|||||||
{: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}
|
||||||
|
<input class="w-full" placeholder="Custom version" on:change={setCustomVersion} bind:value={customVersion} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
|||||||
@@ -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.28",
|
"version": "3.12.32",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
2592
pnpm-lock.yaml
generated
2592
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user