From fffc6b1e4ec919fc5d4bf576aa121d425a8e0444 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 25 Nov 2022 15:44:11 +0100 Subject: [PATCH] feat: docker registries working --- apps/api/src/jobs/deployApplication.ts | 6 +- apps/api/src/lib/buildPacks/common.ts | 7 +- .../routes/api/v1/applications/handlers.ts | 10 ++ .../src/routes/api/v1/applications/index.ts | 4 +- apps/api/src/routes/api/v1/base/index.ts | 8 +- .../src/routes/api/v1/settings/handlers.ts | 37 +++++- apps/api/src/routes/api/v1/settings/index.ts | 6 +- apps/api/src/routes/api/v1/settings/types.ts | 25 ++-- .../routes/applications/[id]/__layout.svelte | 6 +- .../[id]/configuration/registry.svelte | 118 ++++++++++++++++++ .../routes/applications/[id]/danger.svelte | 4 +- .../src/routes/applications/[id]/index.svelte | 21 ++++ apps/ui/src/routes/settings/docker.svelte | 65 +++++++--- 13 files changed, 273 insertions(+), 44 deletions(-) create mode 100644 apps/ui/src/routes/applications/[id]/configuration/registry.svelte diff --git a/apps/api/src/jobs/deployApplication.ts b/apps/api/src/jobs/deployApplication.ts index 706ab1c46..67e93aa5a 100644 --- a/apps/api/src/jobs/deployApplication.ts +++ b/apps/api/src/jobs/deployApplication.ts @@ -4,7 +4,7 @@ import fs from 'fs/promises'; import yaml from 'js-yaml'; import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common'; -import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication } from '../lib/common'; +import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication, isDev } from '../lib/common'; import * as importers from '../lib/importers'; import * as buildpacks from '../lib/buildPacks'; @@ -426,7 +426,9 @@ import * as buildpacks from '../lib/buildPacks'; await saveBuildLog({ line: error, buildId, applicationId: application.id }); } } finally { - await fs.rm(workdir, { recursive: true, force: true }); + if (!isDev) { + await fs.rm(workdir, { recursive: true, force: true }); + } } }); } diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts index 3f2350ec7..0defc1303 100644 --- a/apps/api/src/lib/buildPacks/common.ts +++ b/apps/api/src/lib/buildPacks/common.ts @@ -590,13 +590,14 @@ export async function saveDockerRegistryCredentials({ url, username, password, w } catch (error) { console.log(error); } - await fs.writeFile(`${location}/config.json`, JSON.stringify({ + const payload = JSON.stringify({ "auths": { [url]: { "auth": Buffer.from(`${username}:${decryptedPassword}`).toString('base64') } } - })) + }) + await fs.writeFile(`${location}/config.json`, payload) return location } export async function buildImage({ @@ -626,6 +627,8 @@ export async function buildImage({ const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}` const { dockerRegistry: { url, username, password } } = await prisma.application.findUnique({ where: { id: applicationId }, select: { dockerRegistry: true } }) const location = await saveDockerRegistryCredentials({ url, username, password, workdir }) + console.log(`docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`) + await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}` }) const { status } = await prisma.build.findUnique({ where: { id: buildId } }) diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index e5f4ad198..2370a4445 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -877,6 +877,16 @@ export async function getBuildPack(request) { } } +export async function saveRegistry(request, reply) { + try { + const { id } = request.params + const { registryId } = request.body + await prisma.application.update({ where: { id }, data: { dockerRegistry: { connect: { id: registryId } } } }); + return reply.code(201).send() + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} export async function saveBuildPack(request, reply) { try { const { id } = request.params diff --git a/apps/api/src/routes/api/v1/applications/index.ts b/apps/api/src/routes/api/v1/applications/index.ts index 4ac98d895..750f7b765 100644 --- a/apps/api/src/routes/api/v1/applications/index.ts +++ b/apps/api/src/routes/api/v1/applications/index.ts @@ -1,6 +1,6 @@ import { FastifyPluginAsync } from 'fastify'; import { OnlyId } from '../../../../types'; -import { cancelDeployment, checkDNS, checkDomain, checkRepository, cleanupUnconfiguredApplications, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, getUsageByContainer, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers'; +import { cancelDeployment, checkDNS, checkDomain, checkRepository, cleanupUnconfiguredApplications, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, getUsageByContainer, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRegistry, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers'; import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuilds, GetImages, RestartPreviewApplication, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types'; @@ -64,6 +64,8 @@ const root: FastifyPluginAsync = async (fastify): Promise => { fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request)); fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply)); + fastify.post('/:id/configuration/registry', async (request, reply) => await saveRegistry(request, reply)); + fastify.post('/:id/configuration/database', async (request, reply) => await saveConnectedDatabase(request, reply)); fastify.get('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request)); diff --git a/apps/api/src/routes/api/v1/base/index.ts b/apps/api/src/routes/api/v1/base/index.ts index c0dfeb4b8..170a138f3 100644 --- a/apps/api/src/routes/api/v1/base/index.ts +++ b/apps/api/src/routes/api/v1/base/index.ts @@ -2,7 +2,13 @@ import { FastifyPluginAsync } from 'fastify'; import { errorHandler, listSettings, version } from '../../../../lib/common'; const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => await request.jwtVerify()); + fastify.addHook('onRequest', async (request) => { + try { + await request.jwtVerify() + } catch(error) { + return + } + }); fastify.get('/', async (request) => { const teamId = request.user?.teamId; const settings = await listSettings() diff --git a/apps/api/src/routes/api/v1/settings/handlers.ts b/apps/api/src/routes/api/v1/settings/handlers.ts index 96389000b..044f0349d 100644 --- a/apps/api/src/routes/api/v1/settings/handlers.ts +++ b/apps/api/src/routes/api/v1/settings/handlers.ts @@ -3,7 +3,7 @@ import { X509Certificate } from 'node:crypto'; import type { FastifyReply, FastifyRequest } from 'fastify'; import { asyncExecShell, checkDomainsIsValidInDNS, decrypt, encrypt, errorHandler, isDev, isDNSValid, isDomainConfigured, listSettings, prisma } from '../../../../lib/common'; -import { CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; +import { AddDefaultRegistry, CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; export async function listAllSettings(request: FastifyRequest) { @@ -12,7 +12,7 @@ export async function listAllSettings(request: FastifyRequest) { const settings = await listSettings(); const sshKeys = await prisma.sshKey.findMany({ where: { team: { id: teamId } } }) let publicRegistries = await prisma.dockerRegistry.findMany({ where: { isSystemWide: true } }) - let privateRegistries = await prisma.dockerRegistry.findMany({ where: { team: { id: teamId } } }) + let privateRegistries = await prisma.dockerRegistry.findMany({ where: { team: { id: teamId }, isSystemWide: false } }) publicRegistries = publicRegistries.map((registry) => { if (registry.password) { registry.password = decrypt(registry.password) @@ -149,8 +149,9 @@ export async function saveSSHKey(request: FastifyRequest, reply: Fas } export async function deleteSSHKey(request: FastifyRequest, reply: FastifyReply) { try { + const teamId = request.user.teamId; const { id } = request.body; - await prisma.sshKey.delete({ where: { id } }) + await prisma.sshKey.deleteMany({ where: { id, teamId } }) return reply.code(201).send() } catch ({ status, message }) { return errorHandler({ status, message }) @@ -159,9 +160,10 @@ export async function deleteSSHKey(request: FastifyRequest, reply: export async function deleteCertificates(request: FastifyRequest, reply: FastifyReply) { try { + const teamId = request.user.teamId; const { id } = request.body; await asyncExecShell(`docker exec coolify-proxy sh -c 'rm -f /etc/traefik/acme/custom/${id}-key.pem /etc/traefik/acme/custom/${id}-cert.pem'`) - await prisma.certificate.delete({ where: { id } }) + await prisma.certificate.deleteMany({ where: { id, teamId } }) return reply.code(201).send() } catch ({ status, message }) { return errorHandler({ status, message }) @@ -172,7 +174,7 @@ export async function setDockerRegistry(request: FastifyRequest, reply: FastifyReply) { + try { + const teamId = request.user.teamId; + const { name, url, username, password, isSystemWide } = request.body; + + let encryptedPassword = '' + if (password) encryptedPassword = encrypt(password) + await prisma.dockerRegistry.create({ data: { name, url, username, password: encryptedPassword, isSystemWide, team: { connect: { id: teamId } } } }) + + return reply.code(201).send() + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} +export async function deleteDockerRegistry(request: FastifyRequest, reply: FastifyReply) { + try { + const teamId = request.user.teamId; + const { id } = request.body; + await prisma.application.updateMany({ where: { dockerRegistryId: id }, data: { dockerRegistryId: '0' } }) + await prisma.dockerRegistry.deleteMany({ where: { id, teamId } }) + return reply.code(201).send() + } catch ({ status, message }) { + return errorHandler({ status, message }) + } } \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/settings/index.ts b/apps/api/src/routes/api/v1/settings/index.ts index c93fa9427..d727c123c 100644 --- a/apps/api/src/routes/api/v1/settings/index.ts +++ b/apps/api/src/routes/api/v1/settings/index.ts @@ -2,8 +2,8 @@ import { FastifyPluginAsync } from 'fastify'; import { X509Certificate } from 'node:crypto'; import { encrypt, errorHandler, prisma } from '../../../../lib/common'; -import { checkDNS, checkDomain, deleteCertificates, deleteDomain, deleteSSHKey, listAllSettings, saveSettings, saveSSHKey, setDockerRegistry } from './handlers'; -import { CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; +import { addDockerRegistry, checkDNS, checkDomain, deleteCertificates, deleteDockerRegistry, deleteDomain, deleteSSHKey, listAllSettings, saveSettings, saveSSHKey, setDockerRegistry } from './handlers'; +import { AddDefaultRegistry, CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; const root: FastifyPluginAsync = async (fastify): Promise => { @@ -21,6 +21,8 @@ const root: FastifyPluginAsync = async (fastify): Promise => { fastify.delete('/sshKey', async (request, reply) => await deleteSSHKey(request, reply)); fastify.post('/registry', async (request, reply) => await setDockerRegistry(request, reply)); + fastify.post('/registry/new', async (request, reply) => await addDockerRegistry(request, reply)); + fastify.delete('/registry', async (request, reply) => await deleteDockerRegistry(request, reply)); // fastify.delete<>('/registry', async (request, reply) => await deleteSSHKey(request, reply)); fastify.post('/upload', async (request) => { diff --git a/apps/api/src/routes/api/v1/settings/types.ts b/apps/api/src/routes/api/v1/settings/types.ts index 850128d51..0ae72244f 100644 --- a/apps/api/src/routes/api/v1/settings/types.ts +++ b/apps/api/src/routes/api/v1/settings/types.ts @@ -21,32 +21,32 @@ export interface DeleteDomain { } export interface CheckDomain extends OnlyId { Body: { - fqdn: string, - forceSave: boolean, - dualCerts: boolean, - isDNSCheckEnabled: boolean, + fqdn: string, + forceSave: boolean, + dualCerts: boolean, + isDNSCheckEnabled: boolean, } } export interface CheckDNS { Params: { - domain: string, + domain: string, } } export interface SaveSSHKey { Body: { - privateKey: string, + privateKey: string, name: string } } export interface DeleteSSHKey { Body: { - id: string + id: string } } export interface OnlyIdInBody { Body: { id: string - } + } } export interface SetDefaultRegistry { @@ -55,4 +55,13 @@ export interface SetDefaultRegistry { username: string password: string } +} +export interface AddDefaultRegistry { + Body: { + url: string + name: string + username: string + password: string + isSystemWide: boolean + } } \ No newline at end of file diff --git a/apps/ui/src/routes/applications/[id]/__layout.svelte b/apps/ui/src/routes/applications/[id]/__layout.svelte index e0dcf4eab..b7be25ad6 100644 --- a/apps/ui/src/routes/applications/[id]/__layout.svelte +++ b/apps/ui/src/routes/applications/[id]/__layout.svelte @@ -244,14 +244,14 @@ {/if} {#if $page.url.pathname.startsWith(`/applications/${id}/configuration/`)} -
+
{#if forceDelete} @@ -261,7 +261,7 @@ disabled={!$appSession.isAdmin} class:bg-red-600={$appSession.isAdmin} class:hover:bg-red-500={$appSession.isAdmin} - class="btn btn-sm btn-error text-sm" + class="btn btn-sm btn-error hover:bg-red-700 text-sm w-64" > Delete Application diff --git a/apps/ui/src/routes/applications/[id]/configuration/registry.svelte b/apps/ui/src/routes/applications/[id]/configuration/registry.svelte new file mode 100644 index 000000000..f1e2a9951 --- /dev/null +++ b/apps/ui/src/routes/applications/[id]/configuration/registry.svelte @@ -0,0 +1,118 @@ + + + + +
+
+ {#each registries.public as registry} + + {/each} + {#each registries.private as registry} + + {/each} +
+
diff --git a/apps/ui/src/routes/applications/[id]/danger.svelte b/apps/ui/src/routes/applications/[id]/danger.svelte index ea171a784..373520362 100644 --- a/apps/ui/src/routes/applications/[id]/danger.svelte +++ b/apps/ui/src/routes/applications/[id]/danger.svelte @@ -61,7 +61,7 @@ disabled={!$appSession.isAdmin} class:bg-red-600={$appSession.isAdmin} class:hover:bg-red-500={$appSession.isAdmin} - class="btn btn-lg btn-error text-sm" + class="btn btn-lg btn-error hover:bg-red-700 text-sm w-64" > Force Delete Application @@ -71,7 +71,7 @@ on:click={() => deleteApplication(application.name, false)} type="submit" disabled={!$appSession.isAdmin} - class="btn btn-lg btn-error hover:bg-red-700 text-sm" + class="btn btn-lg btn-error hover:bg-red-700 text-sm w-64" > Delete Application diff --git a/apps/ui/src/routes/applications/[id]/index.svelte b/apps/ui/src/routes/applications/[id]/index.svelte index 325bf0e00..ab943eade 100644 --- a/apps/ui/src/routes/applications/[id]/index.svelte +++ b/apps/ui/src/routes/applications/[id]/index.svelte @@ -522,6 +522,27 @@ > {/if}
+
+ + {#if isDisabled} + + {:else} + + + {/if} +
{#if isDisabled} diff --git a/apps/ui/src/routes/settings/docker.svelte b/apps/ui/src/routes/settings/docker.svelte index 7bef5fc15..a6243a3de 100644 --- a/apps/ui/src/routes/settings/docker.svelte +++ b/apps/ui/src/routes/settings/docker.svelte @@ -21,6 +21,7 @@ import { del, post } from '$lib/api'; import { errorNotification } from '$lib/common'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; + import { addToast } from '$lib/store'; const publicRegistries = registries.public; const privateRegistries = registries.private; @@ -36,9 +37,8 @@ async function handleSubmit() { try { - console.log(newRegistry); - // await post(`/settings/sshKey`, { ...newSSHKey }); - // return window.location.reload(); + await post(`/settings/registry/new`, newRegistry); + return window.location.reload(); } catch (error) { errorNotification(error); return false; @@ -47,14 +47,23 @@ async function setRegistry(registry: any) { try { await post(`/settings/registry`, registry); - } catch (error) {} + return addToast({ + message: 'Registry updated successfully.', + type: 'success' + }); + } catch (error) { + errorNotification(error); + return false; + } } - async function deleteSSHKey(id: string) { - const sure = confirm('Are you sure you would like to delete this SSH key?'); + async function deleteDockerRegistry(id: string) { + const sure = confirm( + 'Are you sure you would like to delete this Docker Registry? All dependent resources will be affected and fails to redeploy.' + ); if (sure) { try { if (!id) return; - // await del(`/settings/sshKey`, { id }); + await del(`/settings/registry`, { id }); return window.location.reload(); } catch (error) { errorNotification(error); @@ -78,7 +87,7 @@ Name - Public + SystemWide Username Password Actions @@ -87,7 +96,7 @@ {#each publicRegistries as registry} - {registry.name} + {registry.name}
{registry.url}
{(registry.isSystemWide && 'Yes') || 'No'} setRegistry(registry)} class="btn btn-sm btn-primary" >Set - {#if !registry.isSystemWide} - deleteDockerRegistry(registry.id)} + class="btn btn-sm btn-error">Delete {/if} @@ -120,15 +130,34 @@ {/each} {#each privateRegistries as registry} - {registry.name} + {registry.name}
{registry.url}
{(registry.isSystemWide && 'Yes') || 'No'} - {registry.username ?? 'N/A'} - {registry.password ?? 'N/A'} + + + - {#if !registry.isSystemWide} - setRegistry(registry)} class="btn btn-sm btn-primary" + >Set + {#if registry.id !== '0'} + {/if}