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) { if (!service.type) {
continue; continue;
} }
console.log(service.type)
let template = templates.find(t => fixType(t.type) === fixType(service.type)); let template = templates.find(t => fixType(t.type) === fixType(service.type));
if (template) { if (template) {
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id)) 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 localPort = await getFreeSSHLocalPort(id);
const { const {
sshKey: { privateKey }, sshKey: { privateKey },
network,
remoteIpAddress, remoteIpAddress,
remotePort, remotePort,
remoteUser remoteUser
} = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } }); } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } });
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 }); await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 });
// Needed for remote docker compose // Needed for remote docker compose
const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell( // const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell(
`ps ax | grep [s]sh-agent | grep coolify-ssh-agent.pid | grep -v grep | wc -l` // `ps ax | grep [s]sh-agent | grep coolify-ssh-agent.pid | grep -v grep | wc -l`
); // );
if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) { // if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) {
try { // try {
await fs.stat(`/tmp/coolify-ssh-agent.pid`); // await fs.stat(`/tmp/coolify-ssh-agent.pid`);
await fs.rm(`/tmp/coolify-ssh-agent.pid`); // await fs.rm(`/tmp/coolify-ssh-agent.pid`);
} catch (error) { } // } catch (error) { }
await asyncExecShell(`eval $(ssh-agent -sa /tmp/coolify-ssh-agent.pid)`); // 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}`); // await asyncExecShell(`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh-add -q ${sshKeyFile}`);
const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell( // const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell(
`ps ax | grep 'ssh -F /dev/null -o StrictHostKeyChecking no -fNL ${localPort}:localhost:${remotePort}' | grep -v grep | wc -l` // `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) { // if (numberOfSSHTunnelsRunning !== '' && Number(numberOfSSHTunnelsRunning.trim()) == 0) {
try { // try {
await asyncExecShell( // await asyncExecShell(
`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}` // `SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}`
); // );
} catch (error) { } // } catch (error) { }
} // }
const config = sshConfig.parse(''); const config = sshConfig.parse('');
const foundWildcard = config.find({ Host: '*' }); const Host = `${remoteIpAddress}-remote`
if (!foundWildcard) {
config.append({ await asyncExecShell(`ssh-keygen -R ${Host}`);
Host: '*', await asyncExecShell(`ssh-keygen -R ${remoteIpAddress}`);
StrictHostKeyChecking: 'no', await asyncExecShell(`ssh-keygen -R localhost:${localPort}`);
ControlMaster: 'auto',
ControlPath: `${homedir}/.ssh/coolify-%r@%h:%p`, const found = config.find({ Host });
ControlPersist: '10m' const foundIp = config.find({ Host: remoteIpAddress });
})
} if (found) config.remove({ Host })
const found = config.find({ Host: remoteIpAddress }); if (foundIp) config.remove({ Host: remoteIpAddress })
if (!found) {
config.append({ config.append({
Host: remoteIpAddress, Host,
Hostname: 'localhost', Hostname: remoteIpAddress,
Port: localPort.toString(), Port: remotePort.toString(),
User: remoteUser, User: remoteUser,
IdentityFile: sshKeyFile, StrictHostKeyChecking: 'no',
StrictHostKeyChecking: 'no' IdentityFile: sshKeyFile,
}); ControlMaster: 'auto',
} ControlPath: `${homedir}/.ssh/coolify-${remoteIpAddress}-%r@%h:%p`,
ControlPersist: '10m'
});
try { try {
await fs.stat(`${homedir}/.ssh/`); await fs.stat(`${homedir}/.ssh/`);
@ -565,27 +568,23 @@ export async function createRemoteEngineConfiguration(id: string) {
} }
export async function executeSSHCmd({ dockerId, command }) { export async function executeSSHCmd({ dockerId, command }) {
const { execaCommand } = await import('execa') 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) { if (remoteEngine) {
await createRemoteEngineConfiguration(dockerId) await createRemoteEngineConfiguration(dockerId)
engine = `ssh://${remoteIpAddress}`
} else {
engine = 'unix:///var/run/docker.sock'
} }
if (process.env.CODESANDBOX_HOST) { if (process.env.CODESANDBOX_HOST) {
if (command.startsWith('docker compose')) { if (command.startsWith('docker compose')) {
command = command.replace(/docker compose/gi, 'docker-compose') command = command.replace(/docker compose/gi, 'docker-compose')
} }
} }
command = `ssh ${remoteIpAddress} ${command}` return await execaCommand(`ssh ${remoteIpAddress}-remote ${command}`)
return await execaCommand(command)
} }
export async function executeDockerCmd({ debug, buildId, applicationId, dockerId, command }: { debug?: boolean, buildId?: string, applicationId?: string, dockerId: string, command: string }): Promise<any> { 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') 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) { if (remoteEngine) {
await createRemoteEngineConfiguration(dockerId); await createRemoteEngineConfiguration(dockerId);
engine = `ssh://${remoteIpAddress}`; engine = `ssh://${remoteIpAddress}-remote`;
} else { } else {
engine = 'unix:///var/run/docker.sock'; engine = 'unix:///var/run/docker.sock';
} }

View File

@ -204,8 +204,8 @@ export async function assignSSHKey(request: FastifyRequest) {
} }
export async function verifyRemoteDockerEngineFn(id: string) { export async function verifyRemoteDockerEngineFn(id: string) {
await createRemoteEngineConfiguration(id); await createRemoteEngineConfiguration(id);
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } }) const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
const host = `ssh://${remoteUser}@${remoteIpAddress}` const host = `ssh://${remoteIpAddress}-remote`
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`); const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
if (!stdout) { if (!stdout) {
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`); 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`); await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
} }
if (isCoolifyProxyUsed) await startTraefikProxy(id); if (isCoolifyProxyUsed) await startTraefikProxy(id);
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
try { try {
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
let daemonJsonParsed = JSON.parse(daemonJson); let daemonJsonParsed = JSON.parse(daemonJson);
let isUpdated = false; let isUpdated = false;
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) { 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 { FastifyPluginAsync } from 'fastify';
import { OnlyId } from '../../../types'; import { OnlyId } from '../../../types';
import { remoteTraefikConfiguration, traefikConfiguration, traefikOtherConfiguration } from './handlers'; import { traefikConfiguration, traefikOtherConfiguration } from './handlers';
import { TraefikOtherConfiguration } from './types'; import { TraefikOtherConfiguration } from './types';
const root: FastifyPluginAsync = async (fastify): Promise<void> => { const root: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.get('/main.json', async (request, reply) => traefikConfiguration(request, reply)); fastify.get<OnlyId>('/main.json', async (request, reply) => traefikConfiguration(request, false));
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request)); 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; export default root;

View File

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

View File

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

View File

@ -6,7 +6,7 @@
{#if $toasts.length > 0} {#if $toasts.length > 0}
<section> <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)} {#each $toasts as toast (toast.id)}
<Toast <Toast
type={toast.type} type={toast.type}

View File

@ -68,12 +68,6 @@
</script> </script>
<div class="w-full relative p-5 "> <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="w-full flex flex-col lg:flex-row space-y-4 lg:space-y-0 space-x-4">
<div class="flex flex-col"> <div class="flex flex-col">
<h1 class="font-bold text-lg lg:text-xl truncate"> <h1 class="font-bold text-lg lg:text-xl truncate">
@ -99,6 +93,11 @@
class="btn btn-sm">Cleanup Storage</button class="btn btn-sm">Cleanup Storage</button
> >
{/if} {/if}
{#if loading.usage}
<button id="streaming" class=" btn btn-sm bg-transparent border-none loading"
>Getting data...</button
>
{/if}
</div> </div>
<div class="flex lg:flex-row flex-col gap-4"> <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" /> <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 statusInterval: any;
let forceDelete = false; let forceDelete = false;
let stopping = false;
const { id } = $page.params; const { id } = $page.params;
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application); $isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
@ -138,17 +138,17 @@
} }
async function stopApplication() { async function stopApplication() {
try { try {
$status.application.initialLoading = true; stopping = true;
await post(`/applications/${id}/stop`, {}); await post(`/applications/${id}/stop`, {});
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} finally { } finally {
$status.application.initialLoading = false; stopping = false;
await getStatus(); await getStatus();
} }
} }
async function getStatus() { async function getStatus() {
if ($status.application.loading) return; if ($status.application.loading && stopping) return;
$status.application.loading = true; $status.application.loading = true;
const data = await get(`/applications/${id}/status`); const data = await get(`/applications/${id}/status`);
@ -298,7 +298,29 @@
Application Error Application Error
</a> </a>
{/if} {/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"> <button class="btn btn-ghost btn-sm gap-2">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -321,7 +343,6 @@
Loading... Loading...
</button> </button>
{:else if $status.application.overallStatus === 'healthy'} {:else if $status.application.overallStatus === 'healthy'}
{#if application.buildPack !== 'compose'} {#if application.buildPack !== 'compose'}
<button <button
on:click={restartApplication} on:click={restartApplication}
@ -370,26 +391,26 @@
Force Redeploy Force Redeploy
</button> </button>
<button <button
on:click={stopApplication} on:click={stopApplication}
type="submit" type="submit"
disabled={!$isDeploymentEnabled} disabled={!$isDeploymentEnabled}
class="btn btn-sm gap-2" 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"
> >
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <svg
<rect x="6" y="5" width="4" height="14" rx="1" /> xmlns="http://www.w3.org/2000/svg"
<rect x="14" y="5" width="4" height="14" rx="1" /> class="w-6 h-6 text-error"
</svg> Stop viewBox="0 0 24 24"
</button> 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/`)} {:else if $isDeploymentEnabled && !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
{#if $status.application.overallStatus === 'degraded'} {#if $status.application.overallStatus === 'degraded'}
<button <button

View File

@ -31,7 +31,7 @@
<div class="flex justify-center px-6 pb-8"> <div class="flex justify-center px-6 pb-8">
<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 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> <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} <button type="submit" class="btn btn-sm bg-destinations w-full lg:w-fit" class:loading disabled={loading}
>{loading >{loading

View File

@ -184,14 +184,16 @@
? 'Verify Remote Docker Engine' ? 'Verify Remote Docker Engine'
: 'Check Remote Docker Engine'}</button : 'Check Remote Docker Engine'}</button
> >
{#if destination.remoteVerified}
<button <button
class="btn btn-sm" class="btn btn-sm"
class:loading={loading.restart} class:loading={loading.restart}
class:bg-error={!loading.restart} class:bg-error={!loading.restart}
disabled={loading.restart} disabled={loading.restart}
on:click|preventDefault={forceRestartProxy}>{$t('destination.force_restart_proxy')}</button on:click|preventDefault={forceRestartProxy}
> >{$t('destination.force_restart_proxy')}</button
>
{/if}
{/if} {/if}
</div> </div>
<div class="grid grid-cols-2 items-center px-10 "> <div class="grid grid-cols-2 items-center px-10 ">