fix: toast, rde, webhooks

This commit is contained in:
Andras Bacsai 2022-11-03 11:32:18 +01:00
parent fa9738a2e0
commit 9dfbbe58ff
12 changed files with 446 additions and 689 deletions

View File

@ -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))

View File

@ -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';
}

View File

@ -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

View File

@ -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;

View File

@ -1,4 +1,4 @@
export interface OnlyId {
Params: { id: string },
Params: { id?: string },
}

View File

@ -4,7 +4,7 @@
export let type = 'info';
function success() {
if (type === 'success') {
return 'bg-coollabs';
return 'bg-dark lg:bg-primary';
}
}
</script>

View File

@ -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}

View File

@ -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" />

View File

@ -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

View File

@ -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

View File

@ -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 ">