commit
b28baaa5aa
@ -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.0.21",
|
"version": "2.0.22",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
"dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
||||||
|
@ -103,9 +103,14 @@ export const getUserDetails = async (event, isAdminRequired = true) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getEngine(engine) {
|
export function getEngine(engine) {
|
||||||
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : `tcp://${engine}:2375`;
|
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export async function saveSshKey(destination) {
|
||||||
|
// return await asyncExecShell(
|
||||||
|
// `echo '${destination.sshPrivateKey}' > /tmp/id_rsa_${destination.id} && chmod 600 /tmp/id_rsa_${destination.id}`
|
||||||
|
// );
|
||||||
|
// }
|
||||||
export async function removeContainer(id, engine) {
|
export async function removeContainer(id, engine) {
|
||||||
const host = getEngine(engine);
|
const host = getEngine(engine);
|
||||||
try {
|
try {
|
||||||
|
@ -15,3 +15,6 @@ export const notNodeDeployments = ['php', 'docker', 'rust'];
|
|||||||
export function getDomain(domain) {
|
export function getDomain(domain) {
|
||||||
return domain?.replace('https://', '').replace('http://', '');
|
return domain?.replace('https://', '').replace('http://', '');
|
||||||
}
|
}
|
||||||
|
export function generateRemoteEngine(destination) {
|
||||||
|
return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`;
|
||||||
|
}
|
||||||
|
21
src/lib/components/svg/applications/Astro.svelte
Normal file
21
src/lib/components/svg/applications/Astro.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<svg
|
||||||
|
class="absolute top-0 left-0 -m-6 h-14 w-14"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
id="a"
|
||||||
|
fill="#302649"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
id="flame"
|
||||||
|
fill="#EF661E"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
6
src/lib/components/svg/applications/Eleventy.svelte
Normal file
6
src/lib/components/svg/applications/Eleventy.svelte
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg viewBox="0 0 128 128" class="absolute top-0 left-0 -m-8 h-16 w-16">
|
||||||
|
<path fill="transparent" d="M18 0h92v128H18z" /><path
|
||||||
|
d="M55.3 36.3h.4c1.1 0 1.5.9 1.5 2.3v41.8c0 1.8-.4 3-1.6 3l-4.8-.1c-1.2 0-1.6-1-1.6-3V45.5l-2.1.5c-1 0-1.5-.8-1.5-2.2v-3c0-1.2.4-2 1.4-2.2l8.3-2.2zm16 36.1l.1 3 .6 1.3.6.6.8.1h2.2c1 0 1.7.8 1.7 2v1.9c0 1.2-.6 2-1.8 2h-3.2l-2.3-.1c-.7-.2-1.4-.5-2.2-1a5.7 5.7 0 01-2-1.9c-.4-.8-.8-1.9-1-3.2-.4-1.4-.5-3-.5-4.8v-16h-1.5c-1.1 0-1.6-1-1.6-2.4v-1.7c0-1.4.5-2.3 1.6-2.3h1.5v-.1l.6-12.3c0-1.5.5-2.5 1.6-2.5h3.1c1.2 0 1.6 1 1.6 2.5v12.3h3.6c1.1 0 1.6 1 1.6 2.4V54c0 1.4-.5 2.3-1.6 2.3h-3.6v16.2zm9.4 15.7c0-2 .3-3.2 1.5-3.2.2 0 .4 0 1.4.4l1.1.3.2-.1.4-.7c.3-.6.4-1.6.4-3l-.6-3.3L79 52.7v-.9c0-1.2.3-2 1.2-2H84c.5 0 1 .3 1.3.6.3.4.5.9.6 1.6l3.4 18 2.6-17.8c.1-.8.3-1.3.6-1.7.3-.4.8-.6 1.3-.6h2.6c1 0 1.4.8 1.4 2l-.1.8L92 82.2c-.5 2.5-1 4.4-1.5 5.7a6.6 6.6 0 01-2 3c-.9.6-1.9.9-3.1.9h-.3c-2 0-3.3-.6-4.1-1.7-.3-.4-.4-1-.4-2zM31.3 38.8l8.2-2.1h.5c1 0 1.4.8 1.4 2.2v41.9c0 1.8-.4 2.9-1.6 2.9h-4.7c-1.2 0-1.6-1.1-1.6-3v-35l-2 .6c-1.2 0-1.6-.9-1.6-2.2v-3c0-1.2.4-2 1.4-2.3z"
|
||||||
|
fill="#FFF"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -119,7 +119,8 @@ export async function getApplicationWebhook({ projectId, branch }) {
|
|||||||
}
|
}
|
||||||
export async function getApplicationById({ id }) {
|
export async function getApplicationById({ id }) {
|
||||||
const body = await prisma.application.findFirst({
|
const body = await prisma.application.findFirst({
|
||||||
where: { id }
|
where: { id },
|
||||||
|
include: { destinationDocker: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...body };
|
return { ...body };
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { asyncExecShell, getEngine } from '$lib/common';
|
import { asyncExecShell, getEngine } from '$lib/common';
|
||||||
|
import { decrypt, encrypt } from '$lib/crypto';
|
||||||
import { dockerInstance } from '$lib/docker';
|
import { dockerInstance } from '$lib/docker';
|
||||||
import { startCoolifyProxy } from '$lib/haproxy';
|
import { startCoolifyProxy } from '$lib/haproxy';
|
||||||
import { getDatabaseImage } from '.';
|
import { getDatabaseImage } from '.';
|
||||||
@ -47,7 +48,36 @@ export async function updateDestination({ id, name, engine, network }) {
|
|||||||
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function newDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
export async function newRemoteDestination({
|
||||||
|
name,
|
||||||
|
teamId,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
}) {
|
||||||
|
const encryptedPrivateKey = encrypt(sshPrivateKey);
|
||||||
|
const destination = await prisma.destinationDocker.create({
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
teams: { connect: { id: teamId } },
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey: encryptedPrivateKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return destination.id;
|
||||||
|
}
|
||||||
|
export async function newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
||||||
const host = getEngine(engine);
|
const host = getEngine(engine);
|
||||||
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
||||||
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
||||||
@ -94,9 +124,13 @@ export async function removeDestination({ id }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getDestination({ id, teamId }) {
|
export async function getDestination({ id, teamId }) {
|
||||||
return await prisma.destinationDocker.findFirst({
|
let destination = await prisma.destinationDocker.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } }
|
where: { id, teams: { some: { id: teamId } } }
|
||||||
});
|
});
|
||||||
|
if (destination.remoteEngine) {
|
||||||
|
destination.sshPrivateKey = decrypt(destination.sshPrivateKey);
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
}
|
}
|
||||||
export async function getDestinationByApplicationId({ id, teamId }) {
|
export async function getDestinationByApplicationId({ id, teamId }) {
|
||||||
return await prisma.destinationDocker.findFirst({
|
return await prisma.destinationDocker.findFirst({
|
||||||
|
@ -187,6 +187,59 @@ export async function reloadHaproxy(engine) {
|
|||||||
const host = getEngine(engine);
|
const host = getEngine(engine);
|
||||||
return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`);
|
return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`);
|
||||||
}
|
}
|
||||||
|
export async function checkProxyConfigurations() {
|
||||||
|
const haproxy = await haproxyInstance();
|
||||||
|
await checkHAProxy(haproxy);
|
||||||
|
try {
|
||||||
|
const stats: any = await haproxy.get(`v2/services/haproxy/stats/native`).json();
|
||||||
|
for (const stat of stats[0].stats) {
|
||||||
|
if (stat.stats.status === 'DOWN' && stat.type === 'server') {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
backend_name: backendName,
|
||||||
|
stats: { lastchg }
|
||||||
|
} = stat;
|
||||||
|
const application = await db.getApplicationById(name);
|
||||||
|
if (!application) {
|
||||||
|
const transactionId = await getNextTransactionId();
|
||||||
|
await haproxy
|
||||||
|
.delete(`v2/services/haproxy/configuration/backends/${backendName}`, {
|
||||||
|
searchParams: {
|
||||||
|
transaction_id: transactionId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
return await completeTransaction(transactionId);
|
||||||
|
}
|
||||||
|
const found = await checkContainer(application.destinationDocker.engine, name);
|
||||||
|
if (!found) {
|
||||||
|
const transactionId = await getNextTransactionId();
|
||||||
|
await haproxy
|
||||||
|
.delete(`v2/services/haproxy/configuration/backends/${backendName}`, {
|
||||||
|
searchParams: {
|
||||||
|
transaction_id: transactionId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
return await completeTransaction(transactionId);
|
||||||
|
}
|
||||||
|
if (lastchg > 120) {
|
||||||
|
const transactionId = await getNextTransactionId();
|
||||||
|
await haproxy
|
||||||
|
.delete(`v2/services/haproxy/configuration/backends/${backendName}`, {
|
||||||
|
searchParams: {
|
||||||
|
transaction_id: transactionId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
await completeTransaction(transactionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
export async function configureProxyForApplication({ domain, imageId, applicationId, port }) {
|
export async function configureProxyForApplication({ domain, imageId, applicationId, port }) {
|
||||||
const haproxy = await haproxyInstance();
|
const haproxy = await haproxyInstance();
|
||||||
await checkHAProxy(haproxy);
|
await checkHAProxy(haproxy);
|
||||||
|
@ -4,7 +4,12 @@ import * as buildpacks from '../buildPacks';
|
|||||||
import * as importers from '../importers';
|
import * as importers from '../importers';
|
||||||
import { dockerInstance } from '../docker';
|
import { dockerInstance } from '../docker';
|
||||||
import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog } from '../common';
|
import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog } from '../common';
|
||||||
import { configureProxyForApplication, reloadHaproxy, setWwwRedirection } from '../haproxy';
|
import {
|
||||||
|
checkProxyConfigurations,
|
||||||
|
configureProxyForApplication,
|
||||||
|
reloadHaproxy,
|
||||||
|
setWwwRedirection
|
||||||
|
} from '../haproxy';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { decrypt } from '$lib/crypto';
|
import { decrypt } from '$lib/crypto';
|
||||||
import { sentry } from '$lib/common';
|
import { sentry } from '$lib/common';
|
||||||
@ -253,6 +258,7 @@ export default async function (job) {
|
|||||||
try {
|
try {
|
||||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||||
saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
|
saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureProxyForApplication({ domain, imageId, applicationId, port });
|
await configureProxyForApplication({ domain, imageId, applicationId, port });
|
||||||
if (isHttps) await letsEncrypt({ domain, id: applicationId });
|
if (isHttps) await letsEncrypt({ domain, id: applicationId });
|
||||||
await setWwwRedirection(fqdn);
|
await setWwwRedirection(fqdn);
|
||||||
|
@ -3,6 +3,7 @@ import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$l
|
|||||||
import { dockerInstance } from '$lib/docker';
|
import { dockerInstance } from '$lib/docker';
|
||||||
import {
|
import {
|
||||||
checkContainer,
|
checkContainer,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureCoolifyProxyOn,
|
configureCoolifyProxyOn,
|
||||||
configureProxyForApplication,
|
configureProxyForApplication,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
@ -13,13 +14,22 @@ import {
|
|||||||
startHttpProxy
|
startHttpProxy
|
||||||
} from '$lib/haproxy';
|
} from '$lib/haproxy';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
|
// import { generateRemoteEngine } from '$lib/components/common';
|
||||||
|
|
||||||
export default async function () {
|
export default async function () {
|
||||||
|
try {
|
||||||
|
await checkProxyConfigurations();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// Check destination containers and configure proxy if needed
|
// Check destination containers and configure proxy if needed
|
||||||
const destinationDockers = await prisma.destinationDocker.findMany({});
|
const destinationDockers = await prisma.destinationDocker.findMany({});
|
||||||
for (const destination of destinationDockers) {
|
for (const destination of destinationDockers) {
|
||||||
if (destination.isCoolifyProxyUsed) {
|
if (destination.isCoolifyProxyUsed) {
|
||||||
|
// if (destination.remoteEngine) {
|
||||||
|
// const engine = generateRemoteEngine(destination);
|
||||||
|
// }
|
||||||
const docker = dockerInstance({ destinationDocker: destination });
|
const docker = dockerInstance({ destinationDocker: destination });
|
||||||
const containers = await docker.engine.listContainers();
|
const containers = await docker.engine.listContainers();
|
||||||
const configurations = containers.filter(
|
const configurations = containers.filter(
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
<script>
|
<script>
|
||||||
export let teams;
|
export let teams;
|
||||||
export let selectedTeamId;
|
export let selectedTeamId;
|
||||||
import { fade } from 'svelte/transition';
|
|
||||||
|
|
||||||
import '../tailwind.css';
|
import '../tailwind.css';
|
||||||
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||||
@ -44,7 +43,6 @@
|
|||||||
let isUpdateAvailable = false;
|
let isUpdateAvailable = false;
|
||||||
let updateStatus = {
|
let updateStatus = {
|
||||||
loading: false,
|
loading: false,
|
||||||
checking: false,
|
|
||||||
success: null
|
success: null
|
||||||
};
|
};
|
||||||
let latestVersion = 'latest';
|
let latestVersion = 'latest';
|
||||||
@ -59,18 +57,14 @@
|
|||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
if ($session.teamId === '0') {
|
if ($session.teamId === '0') {
|
||||||
updateStatus.checking = true;
|
|
||||||
try {
|
try {
|
||||||
const data = await get(`/update.json`);
|
const data = await get(`/update.json`);
|
||||||
if (overrideVersion || data?.isUpdateAvailable) {
|
if (overrideVersion || data?.isUpdateAvailable) {
|
||||||
latestVersion = overrideVersion || data.latestVersion;
|
latestVersion = overrideVersion || data.latestVersion;
|
||||||
isUpdateAvailable = overrideVersion ? true : data?.isUpdateAvailable;
|
isUpdateAvailable = overrideVersion ? true : data?.isUpdateAvailable;
|
||||||
await post(`/update.json`, { type: 'pull', latestVersion });
|
await post(`/update.json`, { type: 'pull', latestVersion, overrideVersion });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
} finally {
|
|
||||||
updateStatus.checking = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -311,32 +305,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-col space-y-4 py-2">
|
<div class="flex flex-col space-y-4 py-2">
|
||||||
{#if $session.teamId === '0'}
|
{#if $session.teamId === '0'}
|
||||||
{#if updateStatus.checking}
|
{#if isUpdateAvailable}
|
||||||
<button
|
|
||||||
disabled
|
|
||||||
in:fade={{ duration: 150 }}
|
|
||||||
class="icons tooltip-right bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
|
|
||||||
data-tooltip="Checking for updates..."
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-9 w-8 animate-spin"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
|
|
||||||
<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
|
|
||||||
<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
|
|
||||||
<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
|
|
||||||
<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
|
|
||||||
<line x1="11" y1="19.94" x2="11" y2="19.95" />
|
|
||||||
</svg></button
|
|
||||||
>
|
|
||||||
{:else if isUpdateAvailable}
|
|
||||||
<button
|
<button
|
||||||
disabled={updateStatus.success === false}
|
disabled={updateStatus.success === false}
|
||||||
data-tooltip="Update available"
|
data-tooltip="Update available"
|
||||||
@ -346,7 +315,7 @@
|
|||||||
{#if updateStatus.loading}
|
{#if updateStatus.loading}
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-8 h-9 lds-heart"
|
class="lds-heart h-9 w-8"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
@ -105,6 +105,8 @@
|
|||||||
foundConfig = findBuildPack('static', packageManager);
|
foundConfig = findBuildPack('static', packageManager);
|
||||||
} else if (indexPHP) {
|
} else if (indexPHP) {
|
||||||
foundConfig = findBuildPack('php');
|
foundConfig = findBuildPack('php');
|
||||||
|
} else {
|
||||||
|
foundConfig = findBuildPack('node', packageManager);
|
||||||
}
|
}
|
||||||
} else if (type === 'github') {
|
} else if (type === 'github') {
|
||||||
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
||||||
@ -146,6 +148,8 @@
|
|||||||
foundConfig = findBuildPack('static', packageManager);
|
foundConfig = findBuildPack('static', packageManager);
|
||||||
} else if (indexPHP) {
|
} else if (indexPHP) {
|
||||||
foundConfig = findBuildPack('php');
|
foundConfig = findBuildPack('php');
|
||||||
|
} else {
|
||||||
|
foundConfig = findBuildPack('node', packageManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -183,9 +187,10 @@
|
|||||||
browser && window.location.reload();
|
browser && window.location.reload();
|
||||||
}
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
if (!foundConfig) foundConfig = findBuildPack('node', packageManager);
|
||||||
|
scanning = false;
|
||||||
}
|
}
|
||||||
if (!foundConfig) foundConfig = findBuildPack('node', packageManager);
|
|
||||||
scanning = false;
|
|
||||||
}
|
}
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await scanRepository();
|
await scanRepository();
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
import Nextjs from '$lib/components/svg/applications/Nextjs.svelte';
|
import Nextjs from '$lib/components/svg/applications/Nextjs.svelte';
|
||||||
import Gatsby from '$lib/components/svg/applications/Gatsby.svelte';
|
import Gatsby from '$lib/components/svg/applications/Gatsby.svelte';
|
||||||
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
||||||
|
import Astro from '$lib/components/svg/applications/Astro.svelte';
|
||||||
|
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
|
||||||
|
|
||||||
const buildPack = application?.buildPack?.toLowerCase();
|
const buildPack = application?.buildPack?.toLowerCase();
|
||||||
</script>
|
</script>
|
||||||
@ -45,6 +47,10 @@
|
|||||||
<Gatsby />
|
<Gatsby />
|
||||||
{:else if buildPack === 'docker'}
|
{:else if buildPack === 'docker'}
|
||||||
<Docker />
|
<Docker />
|
||||||
|
{:else if buildPack === 'astro'}
|
||||||
|
<Astro />
|
||||||
|
{:else if buildPack === 'eleventy'}
|
||||||
|
<Eleventy />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="truncate text-center text-xl font-bold">{application.name}</div>
|
<div class="truncate text-center text-xl font-bold">{application.name}</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
export let state;
|
export let state;
|
||||||
|
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { page } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
@ -125,27 +125,35 @@
|
|||||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
<div class="flex space-x-1 pb-5">
|
<div class="flex space-x-1 pb-5">
|
||||||
<div class="title font-bold">Configuration</div>
|
<div class="title font-bold">Configuration</div>
|
||||||
<button
|
{#if $session.isAdmin}
|
||||||
type="submit"
|
<button
|
||||||
class="bg-sky-600 hover:bg-sky-500"
|
type="submit"
|
||||||
class:bg-sky-600={!loading}
|
class="bg-sky-600 hover:bg-sky-500"
|
||||||
class:hover:bg-sky-500={!loading}
|
class:bg-sky-600={!loading}
|
||||||
disabled={loading}
|
class:hover:bg-sky-500={!loading}
|
||||||
>{loading ? 'Saving...' : 'Save'}
|
disabled={loading}
|
||||||
</button>
|
>{loading ? 'Saving...' : 'Save'}
|
||||||
<button
|
</button>
|
||||||
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
<button
|
||||||
disabled={restarting}
|
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
||||||
on:click|preventDefault={forceRestartProxy}
|
disabled={restarting}
|
||||||
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
on:click|preventDefault={forceRestartProxy}
|
||||||
>
|
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||||
>Scan for applications</button
|
>Scan for applications</button
|
||||||
> -->
|
> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center px-10 ">
|
<div class="grid grid-cols-2 items-center px-10 ">
|
||||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
<input name="name" placeholder="name" bind:value={destination.name} />
|
<input
|
||||||
|
name="name"
|
||||||
|
placeholder="name"
|
||||||
|
disabled={!$session.isAdmin}
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
bind:value={destination.name}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let destination;
|
||||||
|
export let settings;
|
||||||
|
export let state;
|
||||||
|
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import { page, session } from '$app/stores';
|
||||||
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { generateRemoteEngine } from '$lib/components/common';
|
||||||
|
const { id } = $page.params;
|
||||||
|
let cannotDisable = settings.fqdn && destination.engine === '/var/run/docker.sock';
|
||||||
|
// let scannedApps = [];
|
||||||
|
let loading = false;
|
||||||
|
let restarting = false;
|
||||||
|
async function handleSubmit() {
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
return await post(`/destinations/${id}.json`, { ...destination });
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// async function scanApps() {
|
||||||
|
// scannedApps = [];
|
||||||
|
// const data = await fetch(`/destinations/${id}/scan.json`);
|
||||||
|
// const { containers } = await data.json();
|
||||||
|
// scannedApps = containers;
|
||||||
|
// }
|
||||||
|
onMount(async () => {
|
||||||
|
if (state === false && destination.isCoolifyProxyUsed === true) {
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
await stopProxy();
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
} else if (state === true && destination.isCoolifyProxyUsed === false) {
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
await startProxy();
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
async function changeProxySetting() {
|
||||||
|
if (!cannotDisable) {
|
||||||
|
const isProxyActivated = destination.isCoolifyProxyUsed;
|
||||||
|
if (isProxyActivated) {
|
||||||
|
const sure = confirm(
|
||||||
|
`Are you sure you want to ${
|
||||||
|
destination.isCoolifyProxyUsed ? 'disable' : 'enable'
|
||||||
|
} Coolify proxy? It will remove the proxy for all configured networks and all deployments on '${
|
||||||
|
destination.engine
|
||||||
|
}'! Nothing will be reachable if you do it!`
|
||||||
|
);
|
||||||
|
if (!sure) return;
|
||||||
|
}
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
if (isProxyActivated) {
|
||||||
|
await stopProxy();
|
||||||
|
} else {
|
||||||
|
await startProxy();
|
||||||
|
}
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function stopProxy() {
|
||||||
|
try {
|
||||||
|
const engine = generateRemoteEngine(destination);
|
||||||
|
await post(`/destinations/${id}/stop.json`, { engine });
|
||||||
|
return toast.push('Coolify Proxy stopped!');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function startProxy() {
|
||||||
|
try {
|
||||||
|
const engine = generateRemoteEngine(destination);
|
||||||
|
await post(`/destinations/${id}/start.json`, { engine });
|
||||||
|
return toast.push('Coolify Proxy started!');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function forceRestartProxy() {
|
||||||
|
const sure = confirm(
|
||||||
|
'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.'
|
||||||
|
);
|
||||||
|
if (sure) {
|
||||||
|
try {
|
||||||
|
restarting = true;
|
||||||
|
toast.push('Coolify Proxy restarting...');
|
||||||
|
await post(`/destinations/${id}/restart.json`, {
|
||||||
|
engine: destination.engine,
|
||||||
|
fqdn: settings.fqdn
|
||||||
|
});
|
||||||
|
} catch ({ error }) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
|
<div class="flex space-x-1 pb-5">
|
||||||
|
<div class="title font-bold">Configuration</div>
|
||||||
|
{#if $session.isAdmin}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="bg-sky-600 hover:bg-sky-500"
|
||||||
|
class:bg-sky-600={!loading}
|
||||||
|
class:hover:bg-sky-500={!loading}
|
||||||
|
disabled={loading}
|
||||||
|
>{loading ? 'Saving...' : 'Save'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
||||||
|
disabled={restarting}
|
||||||
|
on:click|preventDefault={forceRestartProxy}
|
||||||
|
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||||
|
>Scan for applications</button
|
||||||
|
> -->
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10 ">
|
||||||
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
|
<input
|
||||||
|
name="name"
|
||||||
|
placeholder="name"
|
||||||
|
disabled={!$session.isAdmin}
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
bind:value={destination.name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="engine" class="text-base font-bold text-stone-100">Engine</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="engine"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="engine"
|
||||||
|
placeholder="eg: /var/run/docker.sock"
|
||||||
|
value={destination.engine}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="flex items-center">
|
||||||
|
<label for="remoteEngine">Remote Engine?</label>
|
||||||
|
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
||||||
|
</div> -->
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="network"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="network"
|
||||||
|
placeholder="default: coolify"
|
||||||
|
value={destination.network}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<Setting
|
||||||
|
disabled={cannotDisable}
|
||||||
|
bind:setting={destination.isCoolifyProxyUsed}
|
||||||
|
on:click={changeProxySetting}
|
||||||
|
title="Use Coolify Proxy?"
|
||||||
|
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||||
|
cannotDisable
|
||||||
|
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<!-- <div class="flex justify-center">
|
||||||
|
{#if payload.isCoolifyProxyUsed}
|
||||||
|
{#if state}
|
||||||
|
<button on:click={stopProxy}>Stop proxy</button>
|
||||||
|
{:else}
|
||||||
|
<button on:click={startProxy}>Start proxy</button>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- {#if scannedApps.length > 0}
|
||||||
|
<div class="flex justify-center px-6 pb-10">
|
||||||
|
<div class="flex space-x-2 h-8 items-center">
|
||||||
|
<div class="font-bold text-xl text-white">Found applications</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="max-w-4xl mx-auto px-6">
|
||||||
|
<div class="flex space-x-2 justify-center">
|
||||||
|
{#each scannedApps as app}
|
||||||
|
<FoundApp {app} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if} -->
|
@ -1,4 +1,5 @@
|
|||||||
import { asyncExecShell, getEngine, getTeam, getUserDetails } from '$lib/common';
|
import { asyncExecShell, getUserDetails } from '$lib/common';
|
||||||
|
import { generateRemoteEngine } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import { checkContainer } from '$lib/haproxy';
|
import { checkContainer } from '$lib/haproxy';
|
||||||
@ -12,15 +13,26 @@ export const get: RequestHandler = async (event) => {
|
|||||||
try {
|
try {
|
||||||
const destination = await db.getDestination({ id, teamId });
|
const destination = await db.getDestination({ id, teamId });
|
||||||
const settings = await db.listSettings();
|
const settings = await db.listSettings();
|
||||||
const state =
|
let payload = {
|
||||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
destination,
|
||||||
|
settings,
|
||||||
|
state: false
|
||||||
|
};
|
||||||
|
if (destination.remoteEngine) {
|
||||||
|
// const { stdout } = await asyncExecShell(
|
||||||
|
// `ssh -p ${destination.port} ${destination.user}@${destination.ipAddress} "docker ps -a"`
|
||||||
|
// );
|
||||||
|
// console.log(stdout)
|
||||||
|
// const engine = await generateRemoteEngine(destination);
|
||||||
|
// // await saveSshKey(destination);
|
||||||
|
// payload.state = await checkContainer(engine, 'coolify-haproxy');
|
||||||
|
} else {
|
||||||
|
payload.state =
|
||||||
|
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: { ...payload }
|
||||||
destination,
|
|
||||||
settings,
|
|
||||||
state
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
import type Prisma from '@prisma/client';
|
import type Prisma from '@prisma/client';
|
||||||
import LocalDocker from './_LocalDocker.svelte';
|
import LocalDocker from './_LocalDocker.svelte';
|
||||||
|
import RemoteDocker from './_RemoteDocker.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 text-2xl font-bold">
|
<div class="flex space-x-1 p-6 text-2xl font-bold">
|
||||||
@ -42,6 +43,11 @@
|
|||||||
<span class="arrow-right-applications px-1">></span>
|
<span class="arrow-right-applications px-1">></span>
|
||||||
<span class="pr-2">{destination.name}</span>
|
<span class="pr-2">{destination.name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-4xl px-6">
|
<div class="mx-auto max-w-4xl px-6">
|
||||||
<LocalDocker bind:destination {settings} {state} />
|
{#if destination.remoteEngine}
|
||||||
|
<RemoteDocker bind:destination {settings} {state} />
|
||||||
|
{:else}
|
||||||
|
<LocalDocker bind:destination {settings} {state} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,26 +51,7 @@
|
|||||||
placeholder="eg: /var/run/docker.sock"
|
placeholder="eg: /var/run/docker.sock"
|
||||||
bind:value={payload.engine}
|
bind:value={payload.engine}
|
||||||
/>
|
/>
|
||||||
<!-- <Explainer text="You can use remote Docker Engine with over SSH." /> -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="flex items-center">
|
|
||||||
<label for="remoteEngine">Remote Docker Engine?</label>
|
|
||||||
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
|
||||||
</div>
|
|
||||||
{#if payload.remoteEngine}
|
|
||||||
<div class="grid grid-cols-3 items-center">
|
|
||||||
<label for="user">User</label>
|
|
||||||
<div class="col-span-2">
|
|
||||||
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-3 items-center">
|
|
||||||
<label for="port">Port</label>
|
|
||||||
<div class="col-span-2">
|
|
||||||
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if} -->
|
|
||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
export let payload;
|
||||||
|
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
|
||||||
|
let loading = false;
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const { id } = await post('/new/destination/docker.json', {
|
||||||
|
...payload
|
||||||
|
});
|
||||||
|
return await goto(`/destinations/${id}`);
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex justify-center px-6 pb-8">
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
|
<div class="flex items-center space-x-2 pb-5">
|
||||||
|
<div class="title font-bold">Configuration</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class:bg-sky-600={!loading}
|
||||||
|
class:hover:bg-sky-500={!loading}
|
||||||
|
disabled={loading}
|
||||||
|
>{loading
|
||||||
|
? payload.isCoolifyProxyUsed
|
||||||
|
? 'Saving and configuring proxy...'
|
||||||
|
: 'Saving...'
|
||||||
|
: 'Save'}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
|
<input required name="name" placeholder="name" bind:value={payload.name} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="ipAddress" class="text-base font-bold text-stone-100">IP Address</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
name="ipAddress"
|
||||||
|
placeholder="eg: 192.168..."
|
||||||
|
bind:value={payload.ipAddress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="user" class="text-base font-bold text-stone-100">User</label>
|
||||||
|
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="port" class="text-base font-bold text-stone-100">Port</label>
|
||||||
|
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="sshPrivateKey" class="text-base font-bold text-stone-100">SSH Private Key</label>
|
||||||
|
<textarea
|
||||||
|
rows="10"
|
||||||
|
class="resize-none"
|
||||||
|
required
|
||||||
|
name="sshPrivateKey"
|
||||||
|
placeholder="eg: -----BEGIN...."
|
||||||
|
bind:value={payload.sshPrivateKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
|
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<Setting
|
||||||
|
bind:setting={payload.isCoolifyProxyUsed}
|
||||||
|
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||||
|
title="Use Coolify Proxy?"
|
||||||
|
description="This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).<br><br>Databases will have their own proxy."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -8,10 +8,36 @@ export const post: RequestHandler = async (event) => {
|
|||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { name, engine, network, isCoolifyProxyUsed } = await event.request.json();
|
const {
|
||||||
|
name,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
} = await event.request.json();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const id = await db.newDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
let id = null;
|
||||||
|
if (remoteEngine) {
|
||||||
|
id = await db.newRemoteDestination({
|
||||||
|
name,
|
||||||
|
teamId,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
id = await db.newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
||||||
|
}
|
||||||
return { status: 200, body: { id } };
|
return { status: 200, body: { id } };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -1,25 +1,34 @@
|
|||||||
<script>
|
<script>
|
||||||
import Docker from './_Docker.svelte';
|
import LocalDocker from './_LocalDocker.svelte';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
|
import RemoteDocker from './_RemoteDocker.svelte';
|
||||||
let payload = {};
|
let payload = {};
|
||||||
let selected = 'docker';
|
let selected = 'localDocker';
|
||||||
|
|
||||||
function setPredefined(type) {
|
function setPredefined(type) {
|
||||||
selected = type;
|
selected = type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'docker':
|
case 'localDocker':
|
||||||
payload = {
|
payload = {
|
||||||
name: 'Local Docker',
|
name: 'Local Docker',
|
||||||
engine: '/var/run/docker.sock',
|
engine: '/var/run/docker.sock',
|
||||||
remoteEngine: false,
|
remoteEngine: false,
|
||||||
user: 'root',
|
|
||||||
port: 22,
|
|
||||||
privateKey: null,
|
|
||||||
network: cuid(),
|
network: cuid(),
|
||||||
isCoolifyProxyUsed: true
|
isCoolifyProxyUsed: true
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case 'remoteDocker':
|
||||||
|
payload = {
|
||||||
|
name: 'Remote Docker',
|
||||||
|
remoteEngine: true,
|
||||||
|
ipAddress: null,
|
||||||
|
user: 'root',
|
||||||
|
port: 22,
|
||||||
|
sshPrivateKey: null,
|
||||||
|
network: cuid(),
|
||||||
|
isCoolifyProxyUsed: true
|
||||||
|
};
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -32,12 +41,15 @@
|
|||||||
<div class="flex-col space-y-2 pb-10 text-center">
|
<div class="flex-col space-y-2 pb-10 text-center">
|
||||||
<div class="text-xl font-bold text-white">Predefined destinations</div>
|
<div class="text-xl font-bold text-white">Predefined destinations</div>
|
||||||
<div class="flex justify-center space-x-2">
|
<div class="flex justify-center space-x-2">
|
||||||
<button class="w-32" on:click={() => setPredefined('docker')}>Docker</button>
|
<button class="w-32" on:click={() => setPredefined('localDocker')}>Local Docker</button>
|
||||||
|
<!-- <button class="w-32" on:click={() => setPredefined('remoteDocker')}>Remote Docker</button> -->
|
||||||
<button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button>
|
<button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if selected === 'docker'}
|
{#if selected === 'localDocker'}
|
||||||
<Docker {payload} />
|
<LocalDocker {payload} />
|
||||||
|
{:else if selected === 'remoteDocker'}
|
||||||
|
<RemoteDocker {payload} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-center font-bold text-4xl py-10">Not implemented yet</div>
|
<div class="text-center font-bold text-4xl py-10">Not implemented yet</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection,
|
setWwwRedirection,
|
||||||
@ -95,6 +96,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: consolePort });
|
await configureSimpleServiceProxyOn({ id, domain, port: consolePort });
|
||||||
await db.updateMinioService({ id, publicPort });
|
await db.updateMinioService({ id, publicPort });
|
||||||
await startHttpProxy(destinationDocker, id, publicPort, apiPort);
|
await startHttpProxy(destinationDocker, id, publicPort, apiPort);
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection
|
setWwwRedirection
|
||||||
@ -55,6 +56,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
||||||
|
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection
|
setWwwRedirection
|
||||||
@ -186,6 +187,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
|||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d`
|
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d`
|
||||||
);
|
);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: 8000 });
|
await configureSimpleServiceProxyOn({ id, domain, port: 8000 });
|
||||||
|
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection
|
setWwwRedirection
|
||||||
@ -73,6 +74,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
||||||
|
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection
|
setWwwRedirection
|
||||||
@ -83,6 +84,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
||||||
|
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
import { letsEncrypt } from '$lib/letsencrypt';
|
import { letsEncrypt } from '$lib/letsencrypt';
|
||||||
import {
|
import {
|
||||||
checkHAProxy,
|
checkHAProxy,
|
||||||
|
checkProxyConfigurations,
|
||||||
configureSimpleServiceProxyOn,
|
configureSimpleServiceProxyOn,
|
||||||
reloadHaproxy,
|
reloadHaproxy,
|
||||||
setWwwRedirection
|
setWwwRedirection
|
||||||
@ -120,6 +121,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
await checkProxyConfigurations();
|
||||||
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
||||||
|
|
||||||
if (isHttps) {
|
if (isHttps) {
|
||||||
|
@ -3,6 +3,7 @@ import { getDomain, getUserDetails } from '$lib/common';
|
|||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { listSettings, ErrorHandler } from '$lib/database';
|
import { listSettings, ErrorHandler } from '$lib/database';
|
||||||
import {
|
import {
|
||||||
|
checkProxyConfigurations,
|
||||||
configureCoolifyProxyOff,
|
configureCoolifyProxyOff,
|
||||||
configureCoolifyProxyOn,
|
configureCoolifyProxyOn,
|
||||||
forceSSLOnApplication,
|
forceSSLOnApplication,
|
||||||
@ -79,6 +80,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort } = await event.request.json();
|
const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort } = await event.request.json();
|
||||||
try {
|
try {
|
||||||
|
await checkProxyConfigurations();
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
fqdn: oldFqdn,
|
fqdn: oldFqdn,
|
||||||
|
@ -26,10 +26,12 @@ export const get: RequestHandler = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { type, latestVersion } = await event.request.json();
|
const { type, latestVersion, overrideVersion = false } = await event.request.json();
|
||||||
if (type === 'pull') {
|
if (type === 'pull') {
|
||||||
try {
|
try {
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
|
if (!overrideVersion)
|
||||||
|
await asyncExecShell(`docker image inspect coollabsio/coolify:${latestVersion}`);
|
||||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user