mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-26 12:33:25 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71096acdff | ||
|
|
07da696397 | ||
|
|
41baf150c2 | ||
|
|
f0a52b2ef4 | ||
|
|
54e83fdff1 | ||
|
|
46327ff2fc | ||
|
|
8b26acc841 | ||
|
|
b90cb5a731 | ||
|
|
cd9b642c5e | ||
|
|
41a928d41b | ||
|
|
1388bee62c | ||
|
|
8ebc778d40 | ||
|
|
3a59091b41 | ||
|
|
e764c4651c | ||
|
|
10f04d2177 | ||
|
|
119f994b50 | ||
|
|
e39541c318 | ||
|
|
cf88885c94 | ||
|
|
1192346ce3 | ||
|
|
0e3bd85847 | ||
|
|
edeb6c6965 | ||
|
|
138fd5cb6d | ||
|
|
155410bd44 | ||
|
|
20bd829c2e | ||
|
|
7b7e222946 | ||
|
|
98d901d06c | ||
|
|
4e862cda6f | ||
|
|
4e940807ae | ||
|
|
b081743f54 | ||
|
|
34bb9f301f | ||
|
|
ed8a6daeea | ||
|
|
9e81ab43ac | ||
|
|
32d94cbe97 | ||
|
|
46a83aa457 | ||
|
|
08d7593ca9 | ||
|
|
a50f7a7cc2 | ||
|
|
2c33447f9f | ||
|
|
d67a3f51ec | ||
|
|
2719974262 | ||
|
|
eb5aebd58d | ||
|
|
98dbf3d8a5 | ||
|
|
d9489a2cb4 | ||
|
|
95832d34f7 | ||
|
|
d3e9aea63d | ||
|
|
d6972e2ed1 | ||
|
|
50844e98be | ||
|
|
da86f0076b | ||
|
|
9d8551a9be | ||
|
|
c376123877 | ||
|
|
cd3663038f |
9
.gitpod.yml
Normal file
9
.gitpod.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# This configuration file was automatically generated by Gitpod.
|
||||||
|
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
||||||
|
# and commit this file to your remote git repository to share the goodness with others.
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- init: npm install && npm run build
|
||||||
|
command: npm run start
|
||||||
|
|
||||||
|
|
||||||
@@ -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": "2.9.2",
|
"version": "2.9.10",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
|
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
|
||||||
|
|||||||
@@ -70,16 +70,29 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (compare('2.9.2', version) >= 0) {
|
if (settings.isTraefikUsed) {
|
||||||
// Force stop Coolify Proxy, as it had a bug in < 2.9.2. TrustProxy + api.insecure
|
// Force stop Coolify Proxy, as it had a bug in < 2.9.2. TrustProxy + api.insecure
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`docker stop -t 0 coolify-proxy && docker rm coolify-proxy`);
|
const { stdout } = await asyncExecShell(
|
||||||
const { stdout: Config } = await asyncExecShell(
|
`docker inspect coolify-proxy --format '{{json .Config.Cmd}}'`
|
||||||
`docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
|
||||||
);
|
);
|
||||||
const ip = JSON.parse(Config)[0].Gateway;
|
if (
|
||||||
await asyncExecShell(
|
!stdout
|
||||||
`docker run --restart always \
|
.replaceAll('[', '')
|
||||||
|
.replaceAll(']', '')
|
||||||
|
.replaceAll('"', '')
|
||||||
|
.replace('\n', '')
|
||||||
|
.split(',')
|
||||||
|
.includes('--entrypoints.web.forwardedHeaders.insecure=true')
|
||||||
|
) {
|
||||||
|
console.log('Reconfiguring Coolify Proxy (Traefik)...');
|
||||||
|
await asyncExecShell(`docker stop -t 0 coolify-proxy && docker rm coolify-proxy`);
|
||||||
|
const { stdout: Config } = await asyncExecShell(
|
||||||
|
`docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
||||||
|
);
|
||||||
|
const ip = JSON.parse(Config)[0].Gateway;
|
||||||
|
await asyncExecShell(
|
||||||
|
`docker run --restart always \
|
||||||
--add-host 'host.docker.internal:host-gateway' \
|
--add-host 'host.docker.internal:host-gateway' \
|
||||||
--add-host 'host.docker.internal:${ip}' \
|
--add-host 'host.docker.internal:${ip}' \
|
||||||
-v coolify-traefik-letsencrypt:/etc/traefik/acme \
|
-v coolify-traefik-letsencrypt:/etc/traefik/acme \
|
||||||
@@ -101,7 +114,8 @@ async function main() {
|
|||||||
--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json \
|
--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json \
|
||||||
--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web \
|
--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web \
|
||||||
--log.level=error`
|
--log.level=error`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
}
|
}
|
||||||
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||||
Dockerfile.push(`RUN pip install gunicorn`);
|
Dockerfile.push(`RUN pip install gunicorn`);
|
||||||
|
} else if (pythonWSGI?.toLowerCase() === 'uvicorn') {
|
||||||
|
Dockerfile.push(`RUN pip install uvicorn`);
|
||||||
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
||||||
Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`);
|
Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`);
|
||||||
// Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`)
|
// Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`)
|
||||||
@@ -50,6 +52,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||||
Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`);
|
Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`);
|
||||||
|
} else if (pythonWSGI?.toLowerCase() === 'uvicorn') {
|
||||||
|
Dockerfile.push(`CMD uvicorn ${pythonModule}:${pythonVariable} --port ${port} --host 0.0.0.0`);
|
||||||
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
||||||
Dockerfile.push(
|
Dockerfile.push(
|
||||||
`CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}`
|
`CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}`
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const supportedDatabaseTypesAndVersions = [
|
|||||||
name: 'postgresql',
|
name: 'postgresql',
|
||||||
fancyName: 'PostgreSQL',
|
fancyName: 'PostgreSQL',
|
||||||
baseImage: 'bitnami/postgresql',
|
baseImage: 'bitnami/postgresql',
|
||||||
versions: ['14.2.0', '13.6.0', '12.10.0 ', '11.15.0', '10.20.0']
|
versions: ['14.4.0', '13.6.0', '12.10.0', '11.15.0', '10.20.0']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'redis',
|
name: 'redis',
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
if (installCommand) {
|
if (installCommand) {
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default async function ({
|
|||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git ${workdir}/ && cd ${workdir} && GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git submodule update --init --recursive && GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git lfs pull && cd .. `
|
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||||
);
|
);
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git submodule update --init --recursive && GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git lfs pull && cd .. `
|
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||||
);
|
);
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let application;
|
export let application;
|
||||||
export let appId;
|
export let appId;
|
||||||
|
import Select from 'svelte-select';
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
@@ -33,6 +34,10 @@
|
|||||||
let showSave = false;
|
let showSave = false;
|
||||||
let autodeploy = application.settings.autodeploy || true;
|
let autodeploy = application.settings.autodeploy || true;
|
||||||
|
|
||||||
|
let search = {
|
||||||
|
project: '',
|
||||||
|
branch: ''
|
||||||
|
};
|
||||||
let selected = {
|
let selected = {
|
||||||
group: undefined,
|
group: undefined,
|
||||||
project: undefined,
|
project: undefined,
|
||||||
@@ -84,16 +89,49 @@
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectGroup(event) {
|
||||||
|
selected.group = event.detail;
|
||||||
|
selected.project = null;
|
||||||
|
selected.branch = null;
|
||||||
|
showSave = false;
|
||||||
|
loadProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchProjects(searchText) {
|
||||||
|
if (!selected.group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
search.project = searchText;
|
||||||
|
await loadProjects();
|
||||||
|
return projects;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectProject(event) {
|
||||||
|
selected.project = event.detail;
|
||||||
|
selected.branch = null;
|
||||||
|
showSave = false;
|
||||||
|
loadBranches();
|
||||||
|
}
|
||||||
|
|
||||||
async function loadProjects() {
|
async function loadProjects() {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
page: 1,
|
||||||
|
per_page: 25,
|
||||||
|
archived: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (search.project) {
|
||||||
|
params.append('search', search.project);
|
||||||
|
}
|
||||||
|
|
||||||
loading.projects = true;
|
loading.projects = true;
|
||||||
if (username === selected.group.name) {
|
if (username === selected.group.name) {
|
||||||
try {
|
try {
|
||||||
projects = await get(
|
params.append('min_access_level', 40);
|
||||||
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`,
|
projects = await get(`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`, {
|
||||||
{
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorNotification(error);
|
errorNotification(error);
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
@@ -102,12 +140,9 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
projects = await get(
|
projects = await get(`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`, {
|
||||||
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`,
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
{
|
});
|
||||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorNotification(error);
|
errorNotification(error);
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
@@ -117,11 +152,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function searchBranches(searchText) {
|
||||||
|
if (!selected.project) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
search.branch = searchText;
|
||||||
|
await loadBranches();
|
||||||
|
return branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectBranch(event) {
|
||||||
|
selected.branch = event.detail;
|
||||||
|
isBranchAlreadyUsed();
|
||||||
|
}
|
||||||
|
|
||||||
async function loadBranches() {
|
async function loadBranches() {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
page: 1,
|
||||||
|
per_page: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
if (search.branch) {
|
||||||
|
params.append('search', search.branch);
|
||||||
|
}
|
||||||
|
|
||||||
loading.branches = true;
|
loading.branches = true;
|
||||||
try {
|
try {
|
||||||
branches = await get(
|
branches = await get(
|
||||||
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`,
|
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?${params}`,
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
@@ -267,70 +326,79 @@
|
|||||||
|
|
||||||
<form on:submit={handleSubmit}>
|
<form on:submit={handleSubmit}>
|
||||||
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
|
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
|
||||||
{#if loading.base}
|
<div class="custom-select-wrapper">
|
||||||
<select name="group" disabled class="w-96">
|
<Select
|
||||||
<option selected value="">{$t('application.configuration.loading_groups')}</option>
|
placeholder={loading.base
|
||||||
</select>
|
? $t('application.configuration.loading_groups')
|
||||||
{:else}
|
: $t('application.configuration.select_a_group')}
|
||||||
<select name="group" class="w-96" bind:value={selected.group} on:change={loadProjects}>
|
id="group"
|
||||||
<option value="" disabled selected>{$t('application.configuration.select_a_group')}</option>
|
showIndicator={!loading.base}
|
||||||
{#each groups as group}
|
isWaiting={loading.base}
|
||||||
<option value={group}>{group.full_name}</option>
|
on:select={selectGroup}
|
||||||
{/each}
|
on:clear={() => {
|
||||||
</select>
|
showSave = false;
|
||||||
{/if}
|
projects = [];
|
||||||
{#if loading.projects}
|
branches = [];
|
||||||
<select name="project" disabled class="w-96">
|
selected.group = null;
|
||||||
<option selected value="">{$t('application.configuration.loading_projects')}</option>
|
selected.project = null;
|
||||||
</select>
|
selected.branch = null;
|
||||||
{:else if !loading.projects && projects.length > 0}
|
}}
|
||||||
<select
|
value={selected.group}
|
||||||
name="project"
|
isDisabled={loading.base}
|
||||||
class="w-96"
|
isClearable={false}
|
||||||
bind:value={selected.project}
|
items={groups}
|
||||||
on:change={loadBranches}
|
labelIdentifier="full_name"
|
||||||
disabled={!selected.group}
|
optionIdentifier="id"
|
||||||
>
|
/>
|
||||||
<option value="" disabled selected
|
</div>
|
||||||
>{$t('application.configuration.select_a_project')}</option
|
<div class="custom-select-wrapper">
|
||||||
>
|
<Select
|
||||||
{#each projects as project}
|
placeholder={loading.projects
|
||||||
<option value={project}>{project.name}</option>
|
? $t('application.configuration.loading_projects')
|
||||||
{/each}
|
: $t('application.configuration.select_a_project')}
|
||||||
</select>
|
noOptionsMessage={$t('application.configuration.no_projects_found')}
|
||||||
{:else}
|
id="project"
|
||||||
<select name="project" disabled class="w-96">
|
showIndicator={!loading.projects}
|
||||||
<option disabled selected value=""
|
isWaiting={loading.projects}
|
||||||
>{$t('application.configuration.no_projects_found')}</option
|
isDisabled={loading.projects || !selected.group}
|
||||||
>
|
on:select={selectProject}
|
||||||
</select>
|
on:clear={() => {
|
||||||
{/if}
|
showSave = false;
|
||||||
|
branches = [];
|
||||||
{#if loading.branches}
|
selected.project = null;
|
||||||
<select name="branch" disabled class="w-96">
|
selected.branch = null;
|
||||||
<option selected value="">{$t('application.configuration.loading_branches')}</option>
|
}}
|
||||||
</select>
|
value={selected.project}
|
||||||
{:else if !loading.branches && branches.length > 0}
|
isClearable={false}
|
||||||
<select
|
items={projects}
|
||||||
name="branch"
|
loadOptions={searchProjects}
|
||||||
class="w-96"
|
labelIdentifier="name"
|
||||||
bind:value={selected.branch}
|
optionIdentifier="id"
|
||||||
on:change={isBranchAlreadyUsed}
|
/>
|
||||||
disabled={!selected.project}
|
</div>
|
||||||
>
|
<div class="custom-select-wrapper">
|
||||||
<option value="" disabled selected>{$t('application.configuration.select_a_branch')}</option
|
<Select
|
||||||
>
|
placeholder={loading.branches
|
||||||
{#each branches as branch}
|
? $t('application.configuration.loading_branches')
|
||||||
<option value={branch}>{branch.name}</option>
|
: $t('application.configuration.select_a_branch')}
|
||||||
{/each}
|
noOptionsMessage={$t('application.configuration.no_branches_found')}
|
||||||
</select>
|
id="branch"
|
||||||
{:else}
|
showIndicator={!loading.branches}
|
||||||
<select name="project" disabled class="w-96">
|
isWaiting={loading.branches}
|
||||||
<option disabled selected value=""
|
isDisabled={loading.branches || !selected.project}
|
||||||
>{$t('application.configuration.no_branches_found')}</option
|
on:select={selectBranch}
|
||||||
>
|
on:clear={() => {
|
||||||
</select>
|
showSave = false;
|
||||||
{/if}
|
selected.branch = null;
|
||||||
|
}}
|
||||||
|
value={selected.branch}
|
||||||
|
isClearable={false}
|
||||||
|
items={branches}
|
||||||
|
loadOptions={searchBranches}
|
||||||
|
labelIdentifier="name"
|
||||||
|
optionIdentifier="web_url"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center justify-center space-y-4 pt-5">
|
<div class="flex flex-col items-center justify-center space-y-4 pt-5">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
}
|
}
|
||||||
const endpoint = `/applications/${params.id}.json`;
|
const endpoint = `/applications/${params.id}.json`;
|
||||||
const res = await fetch(endpoint);
|
const res = await fetch(endpoint);
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
@@ -18,7 +17,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: res.status,
|
status: res.status,
|
||||||
error: new Error(`Could not load ${endpoint}`)
|
error: new Error(`Could not load ${endpoint}`)
|
||||||
@@ -39,7 +37,6 @@
|
|||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import Select from 'svelte-select';
|
import Select from 'svelte-select';
|
||||||
|
|
||||||
import Explainer from '$lib/components/Explainer.svelte';
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
import type Prisma from '@prisma/client';
|
import type Prisma from '@prisma/client';
|
||||||
@@ -51,9 +48,7 @@
|
|||||||
import { disabledButton, status } from '$lib/store';
|
import { disabledButton, status } from '$lib/store';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
let domainEl: HTMLInputElement;
|
let domainEl: HTMLInputElement;
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
let usageLoading = false;
|
let usageLoading = false;
|
||||||
@@ -69,12 +64,12 @@
|
|||||||
let previews = application.settings.previews;
|
let previews = application.settings.previews;
|
||||||
let dualCerts = application.settings.dualCerts;
|
let dualCerts = application.settings.dualCerts;
|
||||||
let autodeploy = application.settings.autodeploy;
|
let autodeploy = application.settings.autodeploy;
|
||||||
|
|
||||||
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
let isNonWWWDomainOK = false;
|
let isNonWWWDomainOK = false;
|
||||||
let isWWWDomainOK = false;
|
let isWWWDomainOK = false;
|
||||||
|
|
||||||
$: isDisabled = !$session.isAdmin || $status.application.isRunning;
|
$: isDisabled = !$session.isAdmin || $status.application.isRunning;
|
||||||
|
|
||||||
let wsgis = [
|
let wsgis = [
|
||||||
{
|
{
|
||||||
value: 'None',
|
value: 'None',
|
||||||
@@ -83,6 +78,10 @@
|
|||||||
{
|
{
|
||||||
value: 'Gunicorn',
|
value: 'Gunicorn',
|
||||||
label: 'Gunicorn'
|
label: 'Gunicorn'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'Uvicorn',
|
||||||
|
label: 'Uvicorn'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
function containerClass() {
|
function containerClass() {
|
||||||
@@ -102,7 +101,7 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
||||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
await post(`/applications/${id}.json`, { ...application });
|
await handleSubmit();
|
||||||
}
|
}
|
||||||
domainEl.focus();
|
domainEl.focus();
|
||||||
await getUsage();
|
await getUsage();
|
||||||
@@ -110,7 +109,6 @@
|
|||||||
await getUsage();
|
await getUsage();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function changeSettings(name) {
|
async function changeSettings(name) {
|
||||||
if (name === 'debug') {
|
if (name === 'debug') {
|
||||||
debug = !debug;
|
debug = !debug;
|
||||||
@@ -196,7 +194,6 @@
|
|||||||
application.baseBuildImage = event.detail.value;
|
application.baseBuildImage = event.detail.value;
|
||||||
await handleSubmit();
|
await handleSubmit();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isDNSValid(domain, isWWW) {
|
async function isDNSValid(domain, isWWW) {
|
||||||
try {
|
try {
|
||||||
await get(`/applications/${id}/check.json?domain=${domain}`);
|
await get(`/applications/${id}/check.json?domain=${domain}`);
|
||||||
@@ -218,7 +215,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="text-xs">{application.name} </span>
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if application.fqdn}
|
{#if application.fqdn}
|
||||||
<a
|
<a
|
||||||
href={application.fqdn}
|
href={application.fqdn}
|
||||||
@@ -433,7 +429,6 @@
|
|||||||
<label for="baseBuildImage" class="text-base font-bold text-stone-100"
|
<label for="baseBuildImage" class="text-base font-bold text-stone-100"
|
||||||
>{$t('application.base_build_image')}</label
|
>{$t('application.base_build_image')}</label
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class="custom-select-wrapper">
|
<div class="custom-select-wrapper">
|
||||||
<Select
|
<Select
|
||||||
{isDisabled}
|
{isDisabled}
|
||||||
@@ -530,12 +525,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if application.buildPack === 'python'}
|
{#if application.buildPack === 'python'}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI</label>
|
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label>
|
||||||
<div class="custom-select-wrapper">
|
<div class="custom-select-wrapper">
|
||||||
<Select id="wsgi" items={wsgis} on:select={selectWSGI} value={application.pythonWSGI} />
|
<Select id="wsgi" items={wsgis} on:select={selectWSGI} value={application.pythonWSGI} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="pythonModule" class="text-base font-bold text-stone-100">Module</label>
|
<label for="pythonModule" class="text-base font-bold text-stone-100">Module</label>
|
||||||
<input
|
<input
|
||||||
@@ -544,7 +538,7 @@
|
|||||||
id="pythonModule"
|
id="pythonModule"
|
||||||
required
|
required
|
||||||
bind:value={application.pythonModule}
|
bind:value={application.pythonModule}
|
||||||
placeholder={application.pythonWSGI?.toLowerCase() !== 'gunicorn' ? 'main.py' : 'main'}
|
placeholder={application.pythonWSGI?.toLowerCase() !== 'none' ? 'main' : 'main.py'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if application.pythonWSGI?.toLowerCase() === 'gunicorn'}
|
{#if application.pythonWSGI?.toLowerCase() === 'gunicorn'}
|
||||||
@@ -560,6 +554,19 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if application.pythonWSGI?.toLowerCase() === 'uvicorn'}
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<label for="pythonVariable" class="text-base font-bold text-stone-100">Variable</label>
|
||||||
|
<input
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
name="pythonVariable"
|
||||||
|
id="pythonVariable"
|
||||||
|
required
|
||||||
|
bind:value={application.pythonVariable}
|
||||||
|
placeholder="default: app"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{#if !staticDeployments.includes(application.buildPack)}
|
{#if !staticDeployments.includes(application.buildPack)}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
@@ -694,7 +701,6 @@
|
|||||||
>
|
>
|
||||||
<Explainer text={$t('application.publish_directory_explainer')} />
|
<Explainer text={$t('application.publish_directory_explainer')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
readonly={!$session.isAdmin}
|
readonly={!$session.isAdmin}
|
||||||
name="publishDirectory"
|
name="publishDirectory"
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="text-center font-bold text-xl">
|
<div class="text-center text-xl font-bold">
|
||||||
{$t('application.preview.no_previews_available')}
|
{$t('application.preview.no_previews_available')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => {
|
|||||||
const database = await db.getDatabase({ id, teamId });
|
const database = await db.getDatabase({ id, teamId });
|
||||||
if (database.destinationDockerId) {
|
if (database.destinationDockerId) {
|
||||||
const everStarted = await stopDatabase(database);
|
const everStarted = await stopDatabase(database);
|
||||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
}
|
}
|
||||||
await db.removeDatabase({ id });
|
await db.removeDatabase({ id });
|
||||||
return { status: 200 };
|
return { status: 200 };
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||||
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
|
await stopTcpHttpProxy(id, destinationDocker, oldPublicPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
try {
|
try {
|
||||||
const database = await db.getDatabase({ id, teamId });
|
const database = await db.getDatabase({ id, teamId });
|
||||||
const everStarted = await stopDatabase(database);
|
const everStarted = await stopDatabase(database);
|
||||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
await db.setDatabase({ id, isPublic: false });
|
await db.setDatabase({ id, isPublic: false });
|
||||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
status: 200
|
status: 200
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,7 +74,23 @@
|
|||||||
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`;
|
||||||
await post(`/services/${id}/${service.type}.json`, { ...service });
|
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>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
fider: {
|
fider: {
|
||||||
image: `${image}:${version}`,
|
image: `${image}:${version}`,
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
HOST_DOMAIN: domain,
|
BASE_URL: domain,
|
||||||
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
|
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
|
||||||
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
|
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
|
||||||
EMAIL_NOREPLY: emailNoreply,
|
EMAIL_NOREPLY: emailNoreply,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
image: `${image}:${version}`,
|
image: `${image}:${version}`,
|
||||||
|
volume: `${id}-nc:/usr/app/data`,
|
||||||
environmentVariables: {}
|
environmentVariables: {}
|
||||||
};
|
};
|
||||||
if (serviceSecret.length > 0) {
|
if (serviceSecret.length > 0) {
|
||||||
@@ -41,6 +42,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
container_name: id,
|
container_name: id,
|
||||||
image: config.image,
|
image: config.image,
|
||||||
networks: [network],
|
networks: [network],
|
||||||
|
volumes: [config.volume],
|
||||||
environment: config.environmentVariables,
|
environment: config.environmentVariables,
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
@@ -59,6 +61,11 @@ export const post: RequestHandler = async (event) => {
|
|||||||
[network]: {
|
[network]: {
|
||||||
external: true
|
external: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[config.volume.split(':')[0]]: {
|
||||||
|
name: config.volume.split(':')[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { checkContainer } from '$lib/haproxy';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
function configureMiddleware(
|
function configureMiddleware(
|
||||||
{ id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts },
|
{ id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts, scriptName, type },
|
||||||
traefik
|
traefik
|
||||||
) {
|
) {
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
@@ -125,6 +125,15 @@ function configureMiddleware(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'plausibleanalytics' && scriptName && scriptName !== 'plausible.js') {
|
||||||
|
if (!traefik.http.routers[`${id}`].middlewares.includes(`${id}-redir`)) {
|
||||||
|
traefik.http.routers[`${id}`].middlewares.push(`${id}-redir`);
|
||||||
|
}
|
||||||
|
if (!traefik.http.routers[`${id}-secure`].middlewares.includes(`${id}-redir`)) {
|
||||||
|
traefik.http.routers[`${id}-secure`].middlewares.push(`${id}-redir`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const traefik = {
|
const traefik = {
|
||||||
@@ -176,7 +185,7 @@ export const get: RequestHandler = async (event) => {
|
|||||||
} = application;
|
} = application;
|
||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
const { engine, network } = destinationDocker;
|
const { engine, network } = destinationDocker;
|
||||||
const isRunning = await checkContainer(engine, id);
|
const isRunning = true;
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
@@ -244,7 +253,7 @@ export const get: RequestHandler = async (event) => {
|
|||||||
if (found) {
|
if (found) {
|
||||||
const port = found.ports.main;
|
const port = found.ports.main;
|
||||||
const publicPort = service[type]?.publicPort;
|
const publicPort = service[type]?.publicPort;
|
||||||
const isRunning = await checkContainer(engine, id);
|
const isRunning = true;
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const nakedDomain = domain.replace(/^www\./, '');
|
const nakedDomain = domain.replace(/^www\./, '');
|
||||||
@@ -335,11 +344,6 @@ export const get: RequestHandler = async (event) => {
|
|||||||
replacement: '/js/plausible.js'
|
replacement: '/js/plausible.js'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (traefik.http.routers[id].middlewares.length > 0) {
|
|
||||||
traefik.http.routers[id].middlewares.push(`${id}-redir`);
|
|
||||||
} else {
|
|
||||||
traefik.http.routers[id].middlewares = [`${id}-redir`];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const coolify of data.coolify) {
|
for (const coolify of data.coolify) {
|
||||||
|
|||||||
Reference in New Issue
Block a user