fix: toast, rde, webhooks
This commit is contained in:
parent
fa9738a2e0
commit
9dfbbe58ff
@ -34,7 +34,6 @@ export async function migrateServicesToNewTemplate() {
|
||||
if (!service.type) {
|
||||
continue;
|
||||
}
|
||||
console.log(service.type)
|
||||
let template = templates.find(t => fixType(t.type) === fixType(service.type));
|
||||
if (template) {
|
||||
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id))
|
||||
|
@ -505,56 +505,59 @@ export async function createRemoteEngineConfiguration(id: string) {
|
||||
const localPort = await getFreeSSHLocalPort(id);
|
||||
const {
|
||||
sshKey: { privateKey },
|
||||
network,
|
||||
remoteIpAddress,
|
||||
remotePort,
|
||||
remoteUser
|
||||
} = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } });
|
||||
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 });
|
||||
// Needed for remote docker compose
|
||||
const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell(
|
||||
`ps ax | grep [s]sh-agent | grep coolify-ssh-agent.pid | grep -v grep | wc -l`
|
||||
);
|
||||
if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) {
|
||||
try {
|
||||
await fs.stat(`/tmp/coolify-ssh-agent.pid`);
|
||||
await fs.rm(`/tmp/coolify-ssh-agent.pid`);
|
||||
} catch (error) { }
|
||||
await asyncExecShell(`eval $(ssh-agent -sa /tmp/coolify-ssh-agent.pid)`);
|
||||
}
|
||||
await asyncExecShell(`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh-add -q ${sshKeyFile}`);
|
||||
// const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell(
|
||||
// `ps ax | grep [s]sh-agent | grep coolify-ssh-agent.pid | grep -v grep | wc -l`
|
||||
// );
|
||||
// if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) {
|
||||
// try {
|
||||
// await fs.stat(`/tmp/coolify-ssh-agent.pid`);
|
||||
// await fs.rm(`/tmp/coolify-ssh-agent.pid`);
|
||||
// } catch (error) { }
|
||||
// await asyncExecShell(`eval $(ssh-agent -sa /tmp/coolify-ssh-agent.pid)`);
|
||||
// }
|
||||
// await asyncExecShell(`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh-add -q ${sshKeyFile}`);
|
||||
|
||||
const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell(
|
||||
`ps ax | grep 'ssh -F /dev/null -o StrictHostKeyChecking no -fNL ${localPort}:localhost:${remotePort}' | grep -v grep | wc -l`
|
||||
);
|
||||
if (numberOfSSHTunnelsRunning !== '' && Number(numberOfSSHTunnelsRunning.trim()) == 0) {
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}`
|
||||
);
|
||||
} catch (error) { }
|
||||
}
|
||||
// const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell(
|
||||
// `ps ax | grep 'ssh -F /dev/null -o StrictHostKeyChecking no -fNL ${localPort}:localhost:${remotePort}' | grep -v grep | wc -l`
|
||||
// );
|
||||
// if (numberOfSSHTunnelsRunning !== '' && Number(numberOfSSHTunnelsRunning.trim()) == 0) {
|
||||
// try {
|
||||
// await asyncExecShell(
|
||||
// `SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}`
|
||||
// );
|
||||
// } catch (error) { }
|
||||
// }
|
||||
const config = sshConfig.parse('');
|
||||
const foundWildcard = config.find({ Host: '*' });
|
||||
if (!foundWildcard) {
|
||||
config.append({
|
||||
Host: '*',
|
||||
StrictHostKeyChecking: 'no',
|
||||
ControlMaster: 'auto',
|
||||
ControlPath: `${homedir}/.ssh/coolify-%r@%h:%p`,
|
||||
ControlPersist: '10m'
|
||||
})
|
||||
}
|
||||
const found = config.find({ Host: remoteIpAddress });
|
||||
if (!found) {
|
||||
config.append({
|
||||
Host: remoteIpAddress,
|
||||
Hostname: 'localhost',
|
||||
Port: localPort.toString(),
|
||||
User: remoteUser,
|
||||
IdentityFile: sshKeyFile,
|
||||
StrictHostKeyChecking: 'no'
|
||||
});
|
||||
}
|
||||
const Host = `${remoteIpAddress}-remote`
|
||||
|
||||
await asyncExecShell(`ssh-keygen -R ${Host}`);
|
||||
await asyncExecShell(`ssh-keygen -R ${remoteIpAddress}`);
|
||||
await asyncExecShell(`ssh-keygen -R localhost:${localPort}`);
|
||||
|
||||
const found = config.find({ Host });
|
||||
const foundIp = config.find({ Host: remoteIpAddress });
|
||||
|
||||
if (found) config.remove({ Host })
|
||||
if (foundIp) config.remove({ Host: remoteIpAddress })
|
||||
|
||||
config.append({
|
||||
Host,
|
||||
Hostname: remoteIpAddress,
|
||||
Port: remotePort.toString(),
|
||||
User: remoteUser,
|
||||
StrictHostKeyChecking: 'no',
|
||||
IdentityFile: sshKeyFile,
|
||||
ControlMaster: 'auto',
|
||||
ControlPath: `${homedir}/.ssh/coolify-${remoteIpAddress}-%r@%h:%p`,
|
||||
ControlPersist: '10m'
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.stat(`${homedir}/.ssh/`);
|
||||
@ -565,27 +568,23 @@ export async function createRemoteEngineConfiguration(id: string) {
|
||||
}
|
||||
export async function executeSSHCmd({ dockerId, command }) {
|
||||
const { execaCommand } = await import('execa')
|
||||
let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
|
||||
let { remoteEngine, remoteIpAddress } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
|
||||
if (remoteEngine) {
|
||||
await createRemoteEngineConfiguration(dockerId)
|
||||
engine = `ssh://${remoteIpAddress}`
|
||||
} else {
|
||||
engine = 'unix:///var/run/docker.sock'
|
||||
}
|
||||
if (process.env.CODESANDBOX_HOST) {
|
||||
if (command.startsWith('docker compose')) {
|
||||
command = command.replace(/docker compose/gi, 'docker-compose')
|
||||
}
|
||||
}
|
||||
command = `ssh ${remoteIpAddress} ${command}`
|
||||
return await execaCommand(command)
|
||||
return await execaCommand(`ssh ${remoteIpAddress}-remote ${command}`)
|
||||
}
|
||||
export async function executeDockerCmd({ debug, buildId, applicationId, dockerId, command }: { debug?: boolean, buildId?: string, applicationId?: string, dockerId: string, command: string }): Promise<any> {
|
||||
const { execaCommand } = await import('execa')
|
||||
let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
|
||||
let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
|
||||
if (remoteEngine) {
|
||||
await createRemoteEngineConfiguration(dockerId);
|
||||
engine = `ssh://${remoteIpAddress}`;
|
||||
engine = `ssh://${remoteIpAddress}-remote`;
|
||||
} else {
|
||||
engine = 'unix:///var/run/docker.sock';
|
||||
}
|
||||
|
@ -204,8 +204,8 @@ export async function assignSSHKey(request: FastifyRequest) {
|
||||
}
|
||||
export async function verifyRemoteDockerEngineFn(id: string) {
|
||||
await createRemoteEngineConfiguration(id);
|
||||
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||
const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||
const host = `ssh://${remoteIpAddress}-remote`
|
||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||
if (!stdout) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
@ -215,8 +215,8 @@ export async function verifyRemoteDockerEngineFn(id: string) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
||||
}
|
||||
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
|
||||
try {
|
||||
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
|
||||
let daemonJsonParsed = JSON.parse(daemonJson);
|
||||
let isUpdated = false;
|
||||
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { OnlyId } from '../../../types';
|
||||
import { remoteTraefikConfiguration, traefikConfiguration, traefikOtherConfiguration } from './handlers';
|
||||
import { traefikConfiguration, traefikOtherConfiguration } from './handlers';
|
||||
import { TraefikOtherConfiguration } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/main.json', async (request, reply) => traefikConfiguration(request, reply));
|
||||
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request));
|
||||
fastify.get<OnlyId>('/main.json', async (request, reply) => traefikConfiguration(request, false));
|
||||
fastify.get<OnlyId>('/remote/:id', async (request) => traefikConfiguration(request, true));
|
||||
|
||||
fastify.get<OnlyId>('/remote/:id', async (request) => remoteTraefikConfiguration(request));
|
||||
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
@ -1,4 +1,4 @@
|
||||
export interface OnlyId {
|
||||
Params: { id: string },
|
||||
Params: { id?: string },
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
export let type = 'info';
|
||||
function success() {
|
||||
if (type === 'success') {
|
||||
return 'bg-coollabs';
|
||||
return 'bg-dark lg:bg-primary';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
{#if $toasts.length > 0}
|
||||
<section>
|
||||
<article class="toast toast-top toast-center rounded-none min-w-[18rem]" role="alert">
|
||||
<article class="toast toast-top toast-center rounded-none w-2/3 lg:w-[20rem]" role="alert">
|
||||
{#each $toasts as toast (toast.id)}
|
||||
<Toast
|
||||
type={toast.type}
|
||||
|
@ -68,12 +68,6 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full relative p-5 ">
|
||||
{#if loading.usage}
|
||||
<span class="indicator-item badge bg-yellow-500 badge-sm" />
|
||||
{:else}
|
||||
<span class="indicator-item badge bg-success badge-sm" />
|
||||
{/if}
|
||||
|
||||
<div class="w-full flex flex-col lg:flex-row space-y-4 lg:space-y-0 space-x-4">
|
||||
<div class="flex flex-col">
|
||||
<h1 class="font-bold text-lg lg:text-xl truncate">
|
||||
@ -99,6 +93,11 @@
|
||||
class="btn btn-sm">Cleanup Storage</button
|
||||
>
|
||||
{/if}
|
||||
{#if loading.usage}
|
||||
<button id="streaming" class=" btn btn-sm bg-transparent border-none loading"
|
||||
>Getting data...</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex lg:flex-row flex-col gap-4">
|
||||
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0" />
|
||||
|
@ -75,7 +75,7 @@
|
||||
|
||||
let statusInterval: any;
|
||||
let forceDelete = false;
|
||||
|
||||
let stopping = false;
|
||||
const { id } = $page.params;
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
|
||||
@ -138,17 +138,17 @@
|
||||
}
|
||||
async function stopApplication() {
|
||||
try {
|
||||
$status.application.initialLoading = true;
|
||||
stopping = true;
|
||||
await post(`/applications/${id}/stop`, {});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
$status.application.initialLoading = false;
|
||||
stopping = false;
|
||||
await getStatus();
|
||||
}
|
||||
}
|
||||
async function getStatus() {
|
||||
if ($status.application.loading) return;
|
||||
if ($status.application.loading && stopping) return;
|
||||
$status.application.loading = true;
|
||||
const data = await get(`/applications/${id}/status`);
|
||||
|
||||
@ -298,7 +298,29 @@
|
||||
Application Error
|
||||
</a>
|
||||
{/if}
|
||||
{#if $status.application.initialLoading}
|
||||
{#if stopping}
|
||||
<button class="btn btn-ghost btn-sm gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 animate-spin duration-500 ease-in-out"
|
||||
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>
|
||||
Stopping...
|
||||
</button>
|
||||
{:else if $status.application.initialLoading}
|
||||
<button class="btn btn-ghost btn-sm gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -321,7 +343,6 @@
|
||||
Loading...
|
||||
</button>
|
||||
{:else if $status.application.overallStatus === 'healthy'}
|
||||
|
||||
{#if application.buildPack !== 'compose'}
|
||||
<button
|
||||
on:click={restartApplication}
|
||||
@ -370,26 +391,26 @@
|
||||
Force Redeploy
|
||||
</button>
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-error"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||
</svg> Stop
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-error"
|
||||
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" />
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||
</svg> Stop
|
||||
</button>
|
||||
{:else if $isDeploymentEnabled && !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
|
||||
{#if $status.application.overallStatus === 'degraded'}
|
||||
<button
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<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-start lg:items-center space-x-0 lg:space-x-4 pb-5 flex-col space-y-4 lg:space-y-0">
|
||||
<div class="flex items-start lg:items-center space-x-0 lg:space-x-4 pb-5 flex-col lg:flex-row space-y-4 lg:space-y-0">
|
||||
<div class="title font-bold">{$t('forms.configuration')}</div>
|
||||
<button type="submit" class="btn btn-sm bg-destinations w-full lg:w-fit" class:loading disabled={loading}
|
||||
>{loading
|
||||
|
@ -184,14 +184,16 @@
|
||||
? 'Verify Remote Docker Engine'
|
||||
: 'Check Remote Docker Engine'}</button
|
||||
>
|
||||
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.restart}
|
||||
class:bg-error={!loading.restart}
|
||||
disabled={loading.restart}
|
||||
on:click|preventDefault={forceRestartProxy}>{$t('destination.force_restart_proxy')}</button
|
||||
>
|
||||
{#if destination.remoteVerified}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.restart}
|
||||
class:bg-error={!loading.restart}
|
||||
disabled={loading.restart}
|
||||
on:click|preventDefault={forceRestartProxy}
|
||||
>{$t('destination.force_restart_proxy')}</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10 ">
|
||||
|
Loading…
Reference in New Issue
Block a user