feat: custom certificate
This commit is contained in:
parent
86ac6461d1
commit
90e639f119
@ -21,6 +21,7 @@
|
||||
"@fastify/env": "4.1.0",
|
||||
"@fastify/jwt": "6.3.2",
|
||||
"@fastify/static": "6.5.0",
|
||||
"@fastify/multipart": "7.2.0",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@ladjs/graceful": "3.0.2",
|
||||
"@prisma/client": "4.3.1",
|
||||
@ -49,6 +50,7 @@
|
||||
"p-all": "4.0.0",
|
||||
"p-throttle": "5.0.0",
|
||||
"public-ip": "6.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"ssh-config": "4.1.6",
|
||||
"strip-ansi": "7.0.1",
|
||||
"unique-names-generator": "4.7.1"
|
||||
|
@ -8,6 +8,15 @@ datasource db {
|
||||
url = env("COOLIFY_DATABASE_URL")
|
||||
}
|
||||
|
||||
model Certificate {
|
||||
id String @id @default(cuid())
|
||||
key String
|
||||
cert String
|
||||
team Team[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Setting {
|
||||
id String @id @default(cuid())
|
||||
fqdn String? @unique
|
||||
@ -70,6 +79,7 @@ model Team {
|
||||
gitLabApps GitlabApp[]
|
||||
service Service[]
|
||||
users User[]
|
||||
certificate Certificate[]
|
||||
}
|
||||
|
||||
model TeamInvitation {
|
||||
|
@ -3,6 +3,7 @@ import cors from '@fastify/cors';
|
||||
import serve from '@fastify/static';
|
||||
import env from '@fastify/env';
|
||||
import cookie from '@fastify/cookie';
|
||||
import multipart from '@fastify/multipart';
|
||||
import path, { join } from 'path';
|
||||
import autoLoad from '@fastify/autoload';
|
||||
import { asyncExecShell, createRemoteEngineConfiguration, getDomain, isDev, listSettings, prisma, version } from './lib/common';
|
||||
@ -31,6 +32,7 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
logger: settings?.isAPIDebuggingEnabled || false,
|
||||
trustProxy: true
|
||||
});
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
||||
@ -88,13 +90,13 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
return reply.status(200).sendFile('index.html');
|
||||
});
|
||||
}
|
||||
fastify.register(multipart, { limits: { fileSize: 100000 } });
|
||||
fastify.register(autoLoad, {
|
||||
dir: join(__dirname, 'plugins')
|
||||
});
|
||||
fastify.register(autoLoad, {
|
||||
dir: join(__dirname, 'routes')
|
||||
});
|
||||
|
||||
fastify.register(cookie)
|
||||
fastify.register(cors);
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
@ -145,11 +147,15 @@ prisma.setting.findFirst().then(async (settings) => {
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
|
||||
}, isDev ? 6000 : 60000 * 10)
|
||||
|
||||
// checkProxies
|
||||
// checkProxies and checkFluentBit
|
||||
setInterval(async () => {
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkProxies")
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkFluentBit")
|
||||
}, 10000)
|
||||
|
||||
setInterval(async () => {
|
||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:copySSLCertificates")
|
||||
}, 2000)
|
||||
// cleanupPrismaEngines
|
||||
// setInterval(async () => {
|
||||
// scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupPrismaEngines")
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { parentPort } from 'node:worker_threads';
|
||||
import axios from 'axios';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version, createRemoteEngineConfiguration } from '../lib/common';
|
||||
|
||||
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version, createRemoteEngineConfiguration, decrypt } from '../lib/common';
|
||||
import { checkContainer } from '../lib/docker';
|
||||
import fs from 'fs/promises'
|
||||
async function autoUpdater() {
|
||||
try {
|
||||
const currentVersion = version;
|
||||
@ -39,6 +40,46 @@ async function autoUpdater() {
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
async function checkFluentBit() {
|
||||
if (!isDev) {
|
||||
const engine = '/var/run/docker.sock';
|
||||
const { id } = await prisma.destinationDocker.findFirst({
|
||||
where: { engine, network: 'coolify' }
|
||||
});
|
||||
const { found } = await checkContainer({ dockerId: id, container: 'coolify-fluentbit' });
|
||||
if (!found) {
|
||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
||||
await asyncExecShell(`docker compose up -d fluent-bit`);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function copySSLCertificates() {
|
||||
try {
|
||||
const certificates = await prisma.certificate.findMany({ include: { team: true } })
|
||||
const teamIds = certificates.map(c => c.team.map(t => t.id)).flat()
|
||||
const destinations = await prisma.destinationDocker.findMany({ where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: teamIds } } } } })
|
||||
for (const destination of destinations) {
|
||||
if (destination.remoteEngine) {
|
||||
// TODO: copy certificates to remote engine
|
||||
} else {
|
||||
for (const certificate of certificates) {
|
||||
const { id, key, cert } = certificate
|
||||
const decryptedKey = decrypt(key)
|
||||
await asyncExecShell(`docker exec coolify-proxy sh -c 'mkdir -p /etc/traefik/acme/custom/'`)
|
||||
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey)
|
||||
await fs.writeFile(`/tmp/${id}-cert.pem`, cert)
|
||||
await asyncExecShell(`docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`)
|
||||
await asyncExecShell(`docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`)
|
||||
await fs.rm(`/tmp/${id}-key.pem`)
|
||||
await fs.rm(`/tmp/${id}-cert.pem`)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
async function checkProxies() {
|
||||
try {
|
||||
const { default: isReachable } = await import('is-port-reachable');
|
||||
@ -215,6 +256,14 @@ async function cleanupStorage() {
|
||||
await checkProxies();
|
||||
return;
|
||||
}
|
||||
if (message === 'action:checkFluentBit') {
|
||||
await checkFluentBit();
|
||||
return;
|
||||
}
|
||||
if (message === 'action:copySSLCertificates') {
|
||||
await copySSLCertificates();
|
||||
return;
|
||||
}
|
||||
if (message === 'action:autoUpdater') {
|
||||
if (!status.cleanupStorage) {
|
||||
status.autoUpdater = true
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkUpdate, login, showDashboard, update, resetQueue, getCurrentUser, cleanupManually, restartCoolify } from './handlers';
|
||||
import { GetCurrentUser } from './types';
|
||||
import pump from 'pump'
|
||||
import fs from 'fs'
|
||||
import { asyncExecShell, encrypt, errorHandler, prisma } from '../../../lib/common';
|
||||
|
||||
export interface Update {
|
||||
Body: { latestVersion: string }
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { promises as dns } from 'dns';
|
||||
import { X509Certificate } from 'node:crypto';
|
||||
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkDomainsIsValidInDNS, decrypt, encrypt, errorHandler, getDomain, isDNSValid, isDomainConfigured, listSettings, prisma } from '../../../../lib/common';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, SaveSettings, SaveSSHKey } from './types';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, OnlyIdInBody, SaveSettings, SaveSSHKey } from './types';
|
||||
|
||||
|
||||
export async function listAllSettings(request: FastifyRequest) {
|
||||
@ -16,8 +17,16 @@ export async function listAllSettings(request: FastifyRequest) {
|
||||
unencryptedKeys.push({ id: key.id, name: key.name, privateKey: decrypt(key.privateKey), createdAt: key.createdAt })
|
||||
}
|
||||
}
|
||||
const certificates = await prisma.certificate.findMany({ where: { team: { every: { id: teamId } } } })
|
||||
let cns = [];
|
||||
for (const certificate of certificates) {
|
||||
const x509 = new X509Certificate(certificate.cert);
|
||||
cns.push({ commonName: x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', ''), id: certificate.id, createdAt: certificate.createdAt })
|
||||
}
|
||||
|
||||
return {
|
||||
settings,
|
||||
certificates: cns,
|
||||
sshKeys: unencryptedKeys
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
@ -118,7 +127,7 @@ export async function saveSSHKey(request: FastifyRequest<SaveSSHKey>, reply: Fas
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteSSHKey(request: FastifyRequest<DeleteSSHKey>, reply: FastifyReply) {
|
||||
export async function deleteSSHKey(request: FastifyRequest<OnlyIdInBody>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.body;
|
||||
await prisma.sshKey.delete({ where: { id } })
|
||||
@ -126,4 +135,14 @@ export async function deleteSSHKey(request: FastifyRequest<DeleteSSHKey>, reply:
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteCertificates(request: FastifyRequest<OnlyIdInBody>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.body;
|
||||
await prisma.certificate.delete({ where: { id } })
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
@ -1,21 +1,58 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkDNS, checkDomain, deleteDomain, deleteSSHKey, listAllSettings, saveSettings, saveSSHKey } from './handlers';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, SaveSettings, SaveSSHKey } from './types';
|
||||
import { X509Certificate } from 'node:crypto';
|
||||
|
||||
import { encrypt, errorHandler, prisma } from '../../../../lib/common';
|
||||
import { checkDNS, checkDomain, deleteCertificates, deleteDomain, deleteSSHKey, getCertificates, listAllSettings, saveSettings, saveSSHKey } from './handlers';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, OnlyIdInBody, SaveSettings, SaveSSHKey } from './types';
|
||||
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listAllSettings(request));
|
||||
fastify.post<SaveSettings>('/', async (request, reply) => await saveSettings(request, reply));
|
||||
fastify.delete<DeleteDomain>('/', async (request, reply) => await deleteDomain(request, reply));
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listAllSettings(request));
|
||||
fastify.post<SaveSettings>('/', async (request, reply) => await saveSettings(request, reply));
|
||||
fastify.delete<DeleteDomain>('/', async (request, reply) => await deleteDomain(request, reply));
|
||||
|
||||
fastify.get<CheckDNS>('/check', async (request) => await checkDNS(request));
|
||||
fastify.post<CheckDomain>('/check', async (request) => await checkDomain(request));
|
||||
fastify.get<CheckDNS>('/check', async (request) => await checkDNS(request));
|
||||
fastify.post<CheckDomain>('/check', async (request) => await checkDomain(request));
|
||||
|
||||
fastify.post<SaveSSHKey>('/sshKey', async (request, reply) => await saveSSHKey(request, reply));
|
||||
fastify.delete<DeleteSSHKey>('/sshKey', async (request, reply) => await deleteSSHKey(request, reply));
|
||||
fastify.post<SaveSSHKey>('/sshKey', async (request, reply) => await saveSSHKey(request, reply));
|
||||
fastify.delete<OnlyIdInBody>('/sshKey', async (request, reply) => await deleteSSHKey(request, reply));
|
||||
|
||||
fastify.post('/upload', async (request) => {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const certificates = await prisma.certificate.findMany({})
|
||||
let cns = [];
|
||||
for (const certificate of certificates) {
|
||||
const x509 = new X509Certificate(certificate.cert);
|
||||
cns.push(x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', ''))
|
||||
}
|
||||
const parts = await request.files()
|
||||
let key = null
|
||||
let cert = null
|
||||
for await (const part of parts) {
|
||||
const name = part.fieldname
|
||||
if (name === 'key') key = (await part.toBuffer()).toString()
|
||||
if (name === 'cert') cert = (await part.toBuffer()).toString()
|
||||
}
|
||||
const x509 = new X509Certificate(cert);
|
||||
const cn = x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', '')
|
||||
if (cns.includes(cn)) {
|
||||
throw {
|
||||
message: `A certificate with ${cn} common name already exists.`
|
||||
}
|
||||
}
|
||||
await prisma.certificate.create({ data: { cert, key: encrypt(key), team: { connect: { id: teamId } } } })
|
||||
return { message: 'Certificated uploaded' }
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message });
|
||||
}
|
||||
|
||||
});
|
||||
fastify.delete<OnlyIdInBody>('/certificate', async (request, reply) => await deleteCertificates(request, reply))
|
||||
// fastify.get('/certificates', async (request) => await getCertificates(request))
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
@ -41,4 +41,9 @@ export interface DeleteSSHKey {
|
||||
Body: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
export interface OnlyIdInBody {
|
||||
Body: {
|
||||
id: string
|
||||
}
|
||||
}
|
@ -178,7 +178,19 @@ function configureMiddleware(
|
||||
|
||||
export async function traefikConfiguration(request, reply) {
|
||||
try {
|
||||
const sslpath = '/etc/traefik/acme/custom';
|
||||
const certificates = await prisma.certificate.findMany()
|
||||
let parsedCertificates = []
|
||||
for (const certificate of certificates) {
|
||||
parsedCertificates.push({
|
||||
certFile: `${sslpath}/${certificate.id}-cert.pem`,
|
||||
keyFile: `${sslpath}/${certificate.id}-key.pem`
|
||||
})
|
||||
}
|
||||
const traefik = {
|
||||
tls: {
|
||||
certificates: parsedCertificates
|
||||
},
|
||||
http: {
|
||||
routers: {},
|
||||
services: {},
|
||||
|
@ -42,13 +42,14 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"dayjs": "1.11.5",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.39",
|
||||
"@tailwindcss/typography": "^0.5.7",
|
||||
"cuid": "2.1.8",
|
||||
"daisyui": "2.24.2",
|
||||
"dayjs": "1.11.5",
|
||||
"js-cookie": "3.0.1",
|
||||
"p-limit": "4.0.0",
|
||||
"svelte-file-dropzone": "^1.0.0",
|
||||
"svelte-select": "4.4.7",
|
||||
"sveltekit-i18n": "2.2.2"
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ export function getWebhookUrl(type: string) {
|
||||
async function send({
|
||||
method,
|
||||
path,
|
||||
data = {},
|
||||
data = null,
|
||||
headers,
|
||||
timeout = 120000
|
||||
}: {
|
||||
@ -53,7 +53,7 @@ async function send({
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), timeout);
|
||||
const opts: any = { method, headers: {}, body: null, signal: controller.signal };
|
||||
if (Object.keys(data).length > 0) {
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
const parsedData = data;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (value === '') {
|
||||
@ -85,7 +85,9 @@ async function send({
|
||||
if (dev && !path.startsWith('https://')) {
|
||||
path = `${getAPIUrl()}${path}`;
|
||||
}
|
||||
|
||||
if (method === 'POST' && data && !opts.body) {
|
||||
opts.body = data;
|
||||
}
|
||||
const response = await fetch(`${path}`, opts);
|
||||
|
||||
clearTimeout(id);
|
||||
@ -132,7 +134,7 @@ export function del(
|
||||
|
||||
export function post(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
data: Record<string, unknown> | FormData,
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, any>> {
|
||||
return send({ method: 'POST', path, data, headers });
|
||||
|
@ -4,7 +4,7 @@
|
||||
export let type = 'info';
|
||||
function success() {
|
||||
if (type === 'success') {
|
||||
return 'bg-gradient-to-r from-purple-500 via-pink-500 to-red-500';
|
||||
return 'bg-coollabs';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
20
apps/ui/src/lib/components/Upload.svelte
Normal file
20
apps/ui/src/lib/components/Upload.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { post } from '$lib/api';
|
||||
let cert: any;
|
||||
let key: any;
|
||||
async function submitForm() {
|
||||
const formData = new FormData();
|
||||
formData.append('cert', cert[0]);
|
||||
formData.append('key', key[0]);
|
||||
await post('/upload', formData);
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={submitForm}>
|
||||
<label for="cert">Certificate</label>
|
||||
<input id="cert" type="file" required name="cert" bind:files={cert} />
|
||||
<label for="key">Private Key</label>
|
||||
<input id="key" type="file" required name="key" bind:files={key} />
|
||||
<br />
|
||||
<input type="submit" />
|
||||
</form>
|
@ -226,7 +226,7 @@
|
||||
<a
|
||||
id="settings"
|
||||
sveltekit:prefetch
|
||||
href={$appSession.teamId === '0' ? '/settings/global' : '/settings/ssh-keys'}
|
||||
href={$appSession.teamId === '0' ? '/settings/coolify' : '/settings/ssh'}
|
||||
class="icons hover:text-settings"
|
||||
class:text-settings={$page.url.pathname.startsWith('/settings')}
|
||||
class:bg-coolgray-500={$page.url.pathname.startsWith('/settings')}
|
||||
@ -393,7 +393,7 @@
|
||||
<li>
|
||||
<a
|
||||
class="no-underline icons hover:text-black hover:bg-settings"
|
||||
href={$appSession.teamId === '0' ? '/settings/global' : '/settings/ssh-keys'}
|
||||
href={$appSession.teamId === '0' ? '/settings/coolify' : '/settings/ssh'}
|
||||
class:bg-settings={$page.url.pathname.startsWith('/settings')}
|
||||
class:text-black={$page.url.pathname.startsWith('/settings')}
|
||||
>
|
||||
|
@ -218,7 +218,7 @@
|
||||
id="git"
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-6 h-6"
|
||||
class="w-6 h-6 lg:w-10 lg:h-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
|
@ -61,7 +61,7 @@
|
||||
<div class="pb-2 text-center font-bold">No SSH key found</div>
|
||||
<div class="flex justify-center">
|
||||
<a
|
||||
href="/settings/ssh-keys"
|
||||
href="/settings/ssh"
|
||||
sveltekit:prefetch
|
||||
class="add-icon bg-sky-600 hover:bg-sky-500"
|
||||
>
|
||||
|
@ -3,19 +3,29 @@
|
||||
import { appSession } from '$lib/store';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col pt-4 space-y-6 px-10">
|
||||
<ul class="menu bg-coolgray-200 rounded lg:w-52">
|
||||
{#if $appSession.teamId === '0'}
|
||||
<a
|
||||
href="/settings/global"
|
||||
class="sub-menu no-underline w-full"
|
||||
class:sub-menu-active={$page.routeId === 'settings/global'}
|
||||
<li
|
||||
class="hover:bg-coollabs duration-150"
|
||||
class:bordered={$page.url.pathname === '/settings/coolify'}
|
||||
class:bg-coolgray-500={$page.url.pathname === '/settings/coolify'}
|
||||
>
|
||||
Global Settings
|
||||
</a>
|
||||
<a href="/settings/coolify" class="no-underline w-full">Coolify Settings</a>
|
||||
</li>
|
||||
{/if}
|
||||
<a
|
||||
href="/settings/ssh-keys"
|
||||
class="sub-menu no-underline w-full"
|
||||
class:sub-menu-active={$page.routeId === 'settings/ssh-keys'}>SSH Keys</a
|
||||
|
||||
<li
|
||||
class="hover:bg-coollabs duration-150"
|
||||
class:bordered={$page.url.pathname === '/settings/ssh'}
|
||||
class:bg-coolgray-500={$page.url.pathname === '/settings/ssh'}
|
||||
>
|
||||
</div>
|
||||
<a href="/settings/ssh" class="no-underline w-full">SSH Keys</a>
|
||||
</li>
|
||||
<li
|
||||
class="hover:bg-coollabs duration-150"
|
||||
class:bordered={$page.url.pathname === '/settings/certificates'}
|
||||
class:bg-coolgray-400={$page.url.pathname === '/settings/certificates'}
|
||||
>
|
||||
<a href="/settings/certificates" class="no-underline w-full">SSL Certificates</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script context="module" lang="ts">
|
||||
import { get } from '$lib/api';
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
import Menu from './_Menu.svelte';
|
||||
export const load: Load = async () => {
|
||||
try {
|
||||
const response = await get(`/settings`);
|
||||
@ -19,5 +20,12 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<slot />
|
||||
<div class="flex flex-col lg:flex-row ">
|
||||
<nav class="header flex flex-col w-full lg:w-52">
|
||||
<div class="title pb-10">Settings</div>
|
||||
<Menu />
|
||||
</nav>
|
||||
<div class="pt-0 lg:pt-24 px-5 lg:px-0 mx-auto">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
144
apps/ui/src/routes/settings/certificates.svelte
Normal file
144
apps/ui/src/routes/settings/certificates.svelte
Normal file
@ -0,0 +1,144 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ stuff }) => {
|
||||
try {
|
||||
return {
|
||||
props: {
|
||||
...stuff
|
||||
}
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: 500,
|
||||
error: new Error(error)
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let certificates: any;
|
||||
import { del, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
|
||||
let loading = {
|
||||
save: false
|
||||
};
|
||||
let isModalActive = false;
|
||||
let cert: any = null;
|
||||
let key: any = null;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('cert', cert[0]);
|
||||
formData.append('key', key[0]);
|
||||
await post('/settings/upload', formData);
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async function deleteCertificate(id: string) {
|
||||
const sure = confirm('Are you sure you would like to delete this SSH key?');
|
||||
if (sure) {
|
||||
try {
|
||||
if (!id) return;
|
||||
await del(`/settings/certificate`, { id });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="title font-bold pb-3">SSL Certificates</div>
|
||||
<div class="w-full lg:w-[50em]">
|
||||
{#if certificates.length === 0}
|
||||
<div class="text-sm">No SSL Certificate found</div>
|
||||
<label
|
||||
for="my-modal"
|
||||
class="btn btn-sm bg-settings text-black mt-6"
|
||||
on:click={() => (isModalActive = true)}>Add SSL Certificate</label
|
||||
>
|
||||
{:else}
|
||||
<div class="mx-auto w-full p-6 bg-coolgray-100 rounded border-coolgray-300 border ">
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Common Name</th>
|
||||
<th>CreatedAt</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each certificates as cert}
|
||||
<tr>
|
||||
<td>{cert.commonName}</td>
|
||||
<td>{cert.createdAt}</td>
|
||||
<td
|
||||
><button on:click={() => deleteCertificate(cert.id)} class="btn btn-sm bg-error"
|
||||
>Delete</button
|
||||
></td
|
||||
>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<label
|
||||
for="my-modal"
|
||||
class="btn btn-sm bg-settings text-black mt-6"
|
||||
on:click={() => (isModalActive = true)}>Add SSL Certificate</label
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isModalActive}
|
||||
<input type="checkbox" id="my-modal" class="modal-toggle" />
|
||||
<div class="modal modal-bottom sm:modal-middle ">
|
||||
<div class="modal-box rounded bg-coolgray-300 max-w-2xl">
|
||||
<h3 class="font-bold text-lg">Add a new SSL Certificate</h3>
|
||||
<p class="py-4">
|
||||
SSL Certificates are used to secure your domain and allow you to use HTTPS. <br /><br />Once
|
||||
you uploaded your certificate, Coolify will automatically configure it for you in the
|
||||
background.
|
||||
</p>
|
||||
<div class="modal-action">
|
||||
<form on:submit|preventDefault={handleSubmit} class="w-full">
|
||||
<div class="flex flex-col justify-center">
|
||||
<label for="cert">Certificate</label>
|
||||
<div class="flex-1" />
|
||||
<input
|
||||
class="w-full bg-coolgray-100"
|
||||
id="cert"
|
||||
type="file"
|
||||
required
|
||||
name="cert"
|
||||
bind:files={cert}
|
||||
/>
|
||||
<label for="key" class="pt-10">Private Key</label>
|
||||
<input
|
||||
class="w-full bg-coolgray-100"
|
||||
id="key"
|
||||
type="file"
|
||||
required
|
||||
name="key"
|
||||
bind:files={key}
|
||||
/>
|
||||
</div>
|
||||
<label for="my-modal">
|
||||
<button type="submit" class="btn btn-sm bg-settings text-black mt-4">Upload</button
|
||||
></label
|
||||
>
|
||||
<button on:click={() => (isModalActive = false)} type="button" class="btn btn-sm"
|
||||
>Cancel</button
|
||||
>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@ -18,6 +18,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export let settings: any;
|
||||
export let certificates: any;
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { browser } from '$app/env';
|
||||
@ -26,6 +27,7 @@
|
||||
import { asyncSleep, errorNotification, getDomain } from '$lib/common';
|
||||
import Menu from './_Menu.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Upload from '$lib/components/Upload.svelte';
|
||||
|
||||
let isAPIDebuggingEnabled = settings.isAPIDebuggingEnabled;
|
||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||
@ -194,180 +196,187 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">{$t('index.settings')}</div>
|
||||
</div>
|
||||
<div class="mx-auto w-full">
|
||||
<div class="title font-bold pb-3">Coolify Settings</div>
|
||||
|
||||
<div class="mx-auto w-full p-4 bg-coolgray-100 rounded border-coolgray-300 border">
|
||||
<div class="flex lg:flex-row flex-col">
|
||||
<Menu />
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<div class="flex flex-col lg:flex-row flex-wrap items-center space-x-3 justify-center lg:justify-start lg:py-0 px-4">
|
||||
<div class="title font-bold">{$t('index.global_settings')}</div>
|
||||
<div class="flex lg:flex-row lg:space-x-4 flex-col space-y-2 lg:space-y-0 py-4">
|
||||
<button
|
||||
class="btn btn-sm bg-settings text-black"
|
||||
type="submit"
|
||||
class:bg-orange-600={forceSave}
|
||||
class:hover:bg-orange-400={forceSave}
|
||||
disabled={loading.save}
|
||||
>{loading.save
|
||||
? $t('forms.saving')
|
||||
: forceSave
|
||||
? $t('forms.confirm_continue')
|
||||
: $t('forms.save')}</button
|
||||
>
|
||||
|
||||
{#if isFqdnSet}
|
||||
<button
|
||||
on:click|preventDefault={removeFqdn}
|
||||
disabled={loading.remove}
|
||||
class="btn btn-sm"
|
||||
>{loading.remove ? $t('forms.removing') : $t('forms.remove_domain')}</button
|
||||
>
|
||||
{/if}
|
||||
<button
|
||||
on:click={restartCoolify}
|
||||
class:loading={loading.restart}
|
||||
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
|
||||
>
|
||||
<!-- <Upload />
|
||||
{#if certificates.length > 0}
|
||||
{#each certificates as cert}
|
||||
<div>{cert.commonName}</div>
|
||||
{/each}
|
||||
{/if} -->
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<div class="grid grid-flow-row gap-2 lg:px-10 px-2 pr-5">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
{$t('application.url_fqdn')}
|
||||
<Explainer position="dropdown-bottom" explanation={$t('setting.ssl_explainer')} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 lg:px-10 px-2 pr-5">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
{$t('application.url_fqdn')}
|
||||
<Explainer position="dropdown-bottom" explanation={$t('setting.ssl_explainer')} />
|
||||
</div>
|
||||
<input
|
||||
class="w-full"
|
||||
bind:value={fqdn}
|
||||
readonly={!$appSession.isAdmin || isFqdnSet}
|
||||
disabled={!$appSession.isAdmin || isFqdnSet}
|
||||
on:input={resetView}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="{$t('forms.eg')}: https://coolify.io"
|
||||
/>
|
||||
<input
|
||||
class="w-full"
|
||||
bind:value={fqdn}
|
||||
readonly={!$appSession.isAdmin || isFqdnSet}
|
||||
disabled={!$appSession.isAdmin || isFqdnSet}
|
||||
on:input={resetView}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="{$t('forms.eg')}: https://coolify.io"
|
||||
/>
|
||||
|
||||
{#if forceSave}
|
||||
<div class="flex-col space-y-2 pt-4 text-center">
|
||||
{#if isNonWWWDomainOK}
|
||||
{#if forceSave}
|
||||
<div class="flex-col space-y-2 pt-4 text-center">
|
||||
{#if isNonWWWDomainOK}
|
||||
<button
|
||||
class="btn btn-sm bg-success"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm bg-error"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{#if dualCerts}
|
||||
{#if isWWWDomainOK}
|
||||
<button
|
||||
class="btn btn-sm bg-success"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm bg-error"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{#if dualCerts}
|
||||
{#if isWWWDomainOK}
|
||||
<button
|
||||
class="btn btn-sm bg-success"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm bg-error"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
{$t('forms.public_port_range')}
|
||||
<Explainer explanation={$t('forms.public_port_range_explainer')} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<input
|
||||
class=" w-full px-2"
|
||||
type="number"
|
||||
bind:value={minPort}
|
||||
min="1024"
|
||||
max={maxPort}
|
||||
/>
|
||||
<p>-</p>
|
||||
<input
|
||||
class="w-full px-2"
|
||||
type="number"
|
||||
bind:value={maxPort}
|
||||
min={minPort}
|
||||
max="65543"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isDNSCheckEnabled"
|
||||
bind:setting={isDNSCheckEnabled}
|
||||
title={$t('setting.is_dns_check_enabled')}
|
||||
description={$t('setting.is_dns_check_enabled_explainer')}
|
||||
on:click={() => changeSettings('isDNSCheckEnabled')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
Custom DNS servers <Explainer
|
||||
explanation="You can specify a custom DNS server to verify your domains all over Coolify.<br><br>By default, the OS defined DNS servers are used."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input class="w-full" placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="dualCerts"
|
||||
dataTooltip={$t('setting.must_remove_domain_before_changing')}
|
||||
disabled={isFqdnSet}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
description={$t('setting.generate_www_non_www_ssl')}
|
||||
on:click={() => !isFqdnSet && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isRegistrationEnabled"
|
||||
bind:setting={isRegistrationEnabled}
|
||||
title={$t('setting.registration_allowed')}
|
||||
description={$t('setting.registration_allowed_explainer')}
|
||||
on:click={() => changeSettings('isRegistrationEnabled')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isAPIDebuggingEnabled"
|
||||
bind:setting={isAPIDebuggingEnabled}
|
||||
title="API Debugging"
|
||||
description="Enable API debugging. This will log all API requests and responses.<br><br>You need to restart the Coolify for this to take effect."
|
||||
on:click={() => changeSettings('isAPIDebuggingEnabled')}
|
||||
/>
|
||||
</div>
|
||||
{#if browser && $features.beta}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isAutoUpdateEnabled"
|
||||
bind:setting={isAutoUpdateEnabled}
|
||||
title={$t('setting.auto_update_enabled')}
|
||||
description={$t('setting.auto_update_enabled_explainer')}
|
||||
on:click={() => changeSettings('isAutoUpdateEnabled')}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="dualCerts"
|
||||
dataTooltip={$t('setting.must_remove_domain_before_changing')}
|
||||
disabled={isFqdnSet}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
description={$t('setting.generate_www_non_www_ssl')}
|
||||
on:click={() => !isFqdnSet && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
{$t('forms.public_port_range')}
|
||||
<Explainer explanation={$t('forms.public_port_range_explainer')} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<input
|
||||
class=" w-full px-2"
|
||||
type="number"
|
||||
bind:value={minPort}
|
||||
min="1024"
|
||||
max={maxPort}
|
||||
/>
|
||||
<p>-</p>
|
||||
<input
|
||||
class="w-full px-2"
|
||||
type="number"
|
||||
bind:value={maxPort}
|
||||
min={minPort}
|
||||
max="65543"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isDNSCheckEnabled"
|
||||
bind:setting={isDNSCheckEnabled}
|
||||
title={$t('setting.is_dns_check_enabled')}
|
||||
description={$t('setting.is_dns_check_enabled_explainer')}
|
||||
on:click={() => changeSettings('isDNSCheckEnabled')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div>
|
||||
Custom DNS servers <Explainer
|
||||
explanation="You can specify a custom DNS server to verify your domains all over Coolify.<br><br>By default, the OS defined DNS servers are used."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input class="w-full" placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isRegistrationEnabled"
|
||||
bind:setting={isRegistrationEnabled}
|
||||
title={$t('setting.registration_allowed')}
|
||||
description={$t('setting.registration_allowed_explainer')}
|
||||
on:click={() => changeSettings('isRegistrationEnabled')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isAPIDebuggingEnabled"
|
||||
bind:setting={isAPIDebuggingEnabled}
|
||||
title="API Debugging"
|
||||
description="Enable API debugging. This will log all API requests and responses.<br><br>You need to restart the Coolify for this to take effect."
|
||||
on:click={() => changeSettings('isAPIDebuggingEnabled')}
|
||||
/>
|
||||
</div>
|
||||
{#if browser && $features.beta}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="isAutoUpdateEnabled"
|
||||
bind:setting={isAutoUpdateEnabled}
|
||||
title={$t('setting.auto_update_enabled')}
|
||||
description={$t('setting.auto_update_enabled_explainer')}
|
||||
on:click={() => changeSettings('isAutoUpdateEnabled')}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col lg:flex-row flex-wrap items-center space-x-3 justify-center lg:justify-start lg:py-4 px-4 pb-4 lg:pb-4"
|
||||
>
|
||||
<div class="flex lg:flex-row lg:space-x-4 flex-col space-y-2 lg:space-y-0 px-6">
|
||||
<button
|
||||
class="btn btn-sm bg-settings text-black"
|
||||
type="submit"
|
||||
class:bg-orange-600={forceSave}
|
||||
class:hover:bg-orange-400={forceSave}
|
||||
class:loading={loading.save}
|
||||
disabled={loading.save}
|
||||
>{loading.save
|
||||
? $t('forms.saving')
|
||||
: forceSave
|
||||
? $t('forms.confirm_continue')
|
||||
: $t('forms.save')}</button
|
||||
>
|
||||
|
||||
{#if isFqdnSet}
|
||||
<button
|
||||
on:click|preventDefault={removeFqdn}
|
||||
disabled={loading.remove}
|
||||
class="btn btn-sm"
|
||||
>{loading.remove ? $t('forms.removing') : $t('forms.remove_domain')}</button
|
||||
>
|
||||
{/if}
|
||||
<button
|
||||
on:click={restartCoolify}
|
||||
class:loading={loading.restart}
|
||||
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -2,7 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { appSession } from '$lib/store';
|
||||
if ($appSession.teamId !== '0') {
|
||||
goto('/settings/ssh-keys');
|
||||
goto('/settings/ssh');
|
||||
}
|
||||
goto('/settings/global');
|
||||
goto('/settings/coolify');
|
||||
</script>
|
||||
|
@ -1,155 +0,0 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ stuff }) => {
|
||||
try {
|
||||
return {
|
||||
props: {
|
||||
...stuff
|
||||
}
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: 500,
|
||||
error: new Error(error)
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let sshKeys: any;
|
||||
import { del, post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import Menu from './_Menu.svelte';
|
||||
|
||||
let loading = {
|
||||
save: false
|
||||
};
|
||||
let isModalActive = false;
|
||||
|
||||
let newSSHKey = {
|
||||
name: null,
|
||||
privateKey: null
|
||||
};
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post(`/settings/sshKey`, { ...newSSHKey });
|
||||
return window.location.reload();
|
||||
} 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?');
|
||||
if (sure) {
|
||||
try {
|
||||
if (!id) return;
|
||||
await del(`/settings/sshKey`, { id });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">{$t('index.settings')}</div>
|
||||
</div>
|
||||
<div class="mx-auto w-full">
|
||||
<div class="flex lg:flex-row flex-col">
|
||||
<Menu />
|
||||
<div class="flex flex-col mt-5">
|
||||
<div
|
||||
class="flex flex-col lg:flex-row flex-wrap items-center space-x-3 justify-center lg:justify-start lg:py-0 px-4 pb-4 lg:pb-4"
|
||||
style="min-width: 83vw"
|
||||
>
|
||||
<div class="title font-bold">SSH Keys</div>
|
||||
<button
|
||||
on:click={() => (isModalActive = true)}
|
||||
class="btn btn-sm bg-settings text-black"
|
||||
disabled={loading.save}>New SSH Key</button
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-flow-col gap-2 lg:px-10 px-6">
|
||||
{#if sshKeys.length === 0}
|
||||
<div class="text-sm ">No SSH keys found</div>
|
||||
{:else}
|
||||
{#each sshKeys as key}
|
||||
<div class="box-selection group relative">
|
||||
<div class="text-xl font-bold">{key.name}</div>
|
||||
<div class="py-3 text-stone-600">Added on {key.createdAt}</div>
|
||||
<button on:click={() => deleteSSHKey(key.id)} class="btn btn-sm bg-error"
|
||||
>Delete</button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if isModalActive}
|
||||
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||
<div class="fixed inset-0 bg-coolgray-500 bg-opacity-75 transition-opacity" />
|
||||
<div class="fixed z-10 inset-0 overflow-y-auto text-white">
|
||||
<div class="flex items-end sm:items-center justify-center min-h-full p-4 text-center sm:p-0">
|
||||
<form
|
||||
on:submit|preventDefault={handleSubmit}
|
||||
class="relative bg-coolblack rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full sm:p-6 border border-coolgray-500"
|
||||
>
|
||||
<div class="hidden sm:block absolute top-0 right-0 pt-4 pr-4">
|
||||
<button
|
||||
on:click={() => (isModalActive = false)}
|
||||
type="button"
|
||||
class=" rounded-md text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
>
|
||||
<span class="sr-only">Close</span>
|
||||
<svg
|
||||
class="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium pb-4" id="modal-title">New SSH Key</h3>
|
||||
<div class="text-xs text-stone-400">Add an SSH key to your Coolify instance.</div>
|
||||
<div class="mt-2">
|
||||
<label for="privateKey" class="pb-2">Key</label>
|
||||
<textarea
|
||||
id="privateKey"
|
||||
required
|
||||
bind:value={newSSHKey.privateKey}
|
||||
class="w-full"
|
||||
rows={15}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<label for="name" class="pb-2">Name</label>
|
||||
<input id="name" required bind:value={newSSHKey.name} class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 flex space-x-4 justify-end">
|
||||
<button type="submit" class="btn btn-sm bg-success">Save</button>
|
||||
<button on:click={() => (isModalActive = false)} type="button" class="btn btn-sm"
|
||||
>Cancel</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
145
apps/ui/src/routes/settings/ssh.svelte
Normal file
145
apps/ui/src/routes/settings/ssh.svelte
Normal file
@ -0,0 +1,145 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ stuff }) => {
|
||||
try {
|
||||
return {
|
||||
props: {
|
||||
...stuff
|
||||
}
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: 500,
|
||||
error: new Error(error)
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let sshKeys: any;
|
||||
import { del, post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import Menu from './_Menu.svelte';
|
||||
|
||||
let loading = {
|
||||
save: false
|
||||
};
|
||||
let isModalActive = false;
|
||||
|
||||
let newSSHKey = {
|
||||
name: null,
|
||||
privateKey: null
|
||||
};
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post(`/settings/sshKey`, { ...newSSHKey });
|
||||
return window.location.reload();
|
||||
} 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?');
|
||||
if (sure) {
|
||||
try {
|
||||
if (!id) return;
|
||||
await del(`/settings/sshKey`, { id });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="title font-bold pb-3">SSH Keys</div>
|
||||
<div class="w-full lg:w-[50em]">
|
||||
{#if sshKeys.length === 0}
|
||||
<div class="text-sm">No SSH keys found</div>
|
||||
<label
|
||||
for="my-modal"
|
||||
class="btn btn-sm bg-settings text-black mt-6"
|
||||
on:click={() => (isModalActive = true)}>Add SSH Key</label
|
||||
>
|
||||
{:else}
|
||||
<div
|
||||
class="mx-auto w-full p-6 bg-coolgray-100 rounded border-coolgray-300 border "
|
||||
>
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>CreatedAt</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each sshKeys as key}
|
||||
<tr>
|
||||
<td>{key.name}</td>
|
||||
<td>{key.createdAt}</td>
|
||||
<td
|
||||
><button on:click={() => deleteSSHKey(key.id)} class="btn btn-sm bg-error"
|
||||
>Delete</button
|
||||
></td
|
||||
>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<label
|
||||
for="my-modal"
|
||||
class="btn btn-sm bg-settings text-black mt-6"
|
||||
on:click={() => (isModalActive = true)}>Add SSH Key</label
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isModalActive}
|
||||
<input type="checkbox" id="my-modal" class="modal-toggle" />
|
||||
<div class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box rounded bg-coolgray-300">
|
||||
<h3 class="font-bold text-lg">Add a new SSH Key to Coolify</h3>
|
||||
<p class="py-4">
|
||||
SSH Keys can be used to authenticate & execute commands on remote servers.
|
||||
<br /><br />You can generate a new public/private key using the following command:
|
||||
<br />
|
||||
<br />
|
||||
<code class="bg-coolgray-100 p-2 rounded">ssh-keygen -t rsa -b 4096</code>
|
||||
</p>
|
||||
<div class="modal-action">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<label for="name" class="">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
required
|
||||
bind:value={newSSHKey.name}
|
||||
class="w-full bg-coolgray-100"
|
||||
/>
|
||||
<label for="privateKey" class="pt-4">Private Key</label>
|
||||
<textarea
|
||||
id="privateKey"
|
||||
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
|
||||
required
|
||||
bind:value={newSSHKey.privateKey}
|
||||
class="w-full bg-coolgray-100"
|
||||
rows={15}
|
||||
/>
|
||||
<label for="my-modal">
|
||||
<button type="submit" class="btn btn-sm bg-settings text-black mt-4">Save</button
|
||||
></label
|
||||
>
|
||||
<button on:click={() => (isModalActive = false)} type="button" class="btn btn-sm"
|
||||
>Cancel</button
|
||||
>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
32
others/cert.pem
Normal file
32
others/cert.pem
Normal file
@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
52
others/key.pem
Normal file
52
others/key.pem
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
dVCwMJnAx42wCGRYCQnZtOpWVE40
|
||||
-----END PRIVATE KEY-----
|
33
others/morethanone_cert.pem
Normal file
33
others/morethanone_cert.pem
Normal file
@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFrTCCA5WgAwIBAgIUWscetQdy/o3JGjsR1LifMVHSFPYwDQYJKoZIhvcNAQEL
|
||||
BQAwZTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEeMBwGA1UEAwwVbm9kZS5jb20sd3d3
|
||||
Lm5vZGUuY29tMCAXDTIyMDkyMTA5MDgzOVoYDzIwNTAwMjA2MDkwODM5WjBlMQsw
|
||||
CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu
|
||||
ZXQgV2lkZ2l0cyBQdHkgTHRkMR4wHAYDVQQDDBVub2RlLmNvbSx3d3cubm9kZS5j
|
||||
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC91LQflL+X2YU1oiDl
|
||||
V9xGKtkUM7KOZgCQSkwDdkxF6fzjOAkjnekWkPRvPijWLuOLuqbMYZe8uEw06DgL
|
||||
JlwcYBKC8hrBsaT2QpbzDuetzsYeudfBVYfLSHiUOvP+pVf2nurprbHPxdWLmh8v
|
||||
HFLmTSVAH0sDDRb3gt2S7OnrGjw4fSjfS/5lFzGdV4ic1vFt2bB9Fv+D5pKPO/Zm
|
||||
O9XIhxZ8/LBNTRhR6TEWn3h0HMVqTMKVm3cjDB+xelivZ8LCV7sO2Y/ZS70ByI2K
|
||||
op4e7H50uNh4WjhSXNBSabLNw1gC/cvBLn0wypGtKgRt26Nj4FNvpfoXfbwCDkgJ
|
||||
SFyrKLaL1YraNZOxIWgcQs2dKoVYKQfLBQeL+uSe26OY5eXZUcq4cmQjzeahF0MM
|
||||
7STR6c14QuR0ppgInpuLGFVmcnCQBhpR7P2Y3ITIGqQto/ZHAWvx+BgkdUhhKoFO
|
||||
6YUvBiG14CznOVct2TStt/RyW+Z3NwOd8Bh0MA5TFEdv4wfkVBkd7d1ICvF9+hCz
|
||||
2LbVztZ4lkqjSooAFcz/xm1dXSNwhRwwG6sOAm4XC+7Pt6Oe5pq9ahFQKW+zkLr2
|
||||
TKQ8Ppr7wQW6IrrWola4MUeQ+DE8l4QKlLusvejPTdRsnnEt0RUPoWcACB1e0CzO
|
||||
3zOd300xy0ESeeHLLFuaG5FdOwIDAQABo1MwUTAdBgNVHQ4EFgQUhZdMK4poQSOG
|
||||
55sANxJlTMTCtEcwHwYDVR0jBBgwFoAUhZdMK4poQSOG55sANxJlTMTCtEcwDwYD
|
||||
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAJNPRgCNHbSR+UYKFSet4
|
||||
XscYjtxb59QxFqN+AqUiEh38SVzWo2nhnDZxiDsTK2nA0qSrseoMTnTmWJuEf+io
|
||||
n3XmUNgalxzV0QGMhQZPT3hyoPditwc7easnm2Jwd10kkhMdlXnfy3lgHPIMH6/E
|
||||
0jh3sSuEora9Q3jIdobRQALrHI++lsLdOcJy0A2LKkVWbvftz6B2cDC6M+QhwPGo
|
||||
QNIuI8Cl2J89mNsDtZRohbA2/iUGE2Y47m9OD8snLOOZmhiCHsq82AYEt0wAqPI1
|
||||
biPvN/F+S4lMj71ue77BOg6sKKvotSgJQvyUO4iAouHoxxo5luT1nz2i2ofdvHFd
|
||||
Lm3eth9hEykOEi7AIJHSlXcFEdlEaP+ohOcTDoaoZc4kqYxuEDpC6otaQhHvqS/D
|
||||
0JGjdVTw7q0msfzG/7a5k4hklgB4JHrzWS++sCFKAu6LYnsVVFKu6P9qQxFv86qi
|
||||
xpWX9sj+7+qw1qPjSNKx9S6Lx+2vMIG/2l1rsJIKMLC4ndnT3bjwme0YS0/iOEi3
|
||||
EQT//Mb6Hg1R4r2OlvfYSt+pGOa64UwgqHj8wOLIkb/txsF3mQDbDE16tByih83E
|
||||
Wl44xDuoTuCZsvwNh9PJtg1XRaIdXwZFauRwV3nyLREeDX3EESmOyXYZZljcS9/K
|
||||
Gw265N/AwFB26FAC4Mof6FI=
|
||||
-----END CERTIFICATE-----
|
52
others/morethanone_key.pem
Normal file
52
others/morethanone_key.pem
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
oRPGJSpHWgCoMeHba1Q7nGLmwvHg9w==
|
||||
-----END PRIVATE KEY-----
|
32
others/wildcard_cert.pem
Normal file
32
others/wildcard_cert.pem
Normal file
@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFlzCCA3+gAwIBAgIUJQM7MYyRjtvlVYzVqog0JGigp7swDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKKi5ub2RlLmNvbTAg
|
||||
Fw0yMjA5MjEwOTA3MDVaGA8yMDUwMDIwNjA5MDcwNVowWjELMAkGA1UEBhMCQVUx
|
||||
EzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
|
||||
UHR5IEx0ZDETMBEGA1UEAwwKKi5ub2RlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggIPADCCAgoCggIBAK62Sk9JUSQXv+wk5FztR1L0H8IgK34th5dOADV0zwY0Olnm
|
||||
ukkjI2cljNaY3DgfhGdv6m+3sozMliRjtmOkLtWoGsK8Bwv6lZrF6GPy2N0IeIQ5
|
||||
tddyaENm5ua7rG3kgYCBItadOg33A6eUWFYmZi5K7E9fpHUS7JCi+tYAaHqaESfI
|
||||
z2wAzlLo+nyfn6AzAWkcdtsNtwCJOXbC6gvJIjPwdsM9wJ77m1k32Dwo2ZJ4BAQm
|
||||
JoHNZaIy3pytZWaoIHnKwioIF/xKALls6A8TQqetPFxUfGQAsKqMFF/nbzn+S4GE
|
||||
ltufX/E1KECNiircHWC998XqLoaBim6d9+MKcyBZqG+Gr6k4JGLqw+FTVdCKo0hO
|
||||
L0GWA+TWZbDtCBmR2fEI8UJ3wnmDjzGir/9Yi/ugmovPQ5lpQIhEQWTQ/zGgVP38
|
||||
xVTYEPAdVVjFRnFhLlmRDedYo552A68KBlZPPotra5OustB6Rop9z4kNxH7mGDQg
|
||||
GdS6B0XbJUSk4325+7OQZpp30ursh+f4X0dVyC4maPjUC0YZuMbvoTLwwmu7zrtM
|
||||
NpvlLhcdBaBfKLdU6/bAxd7V8VHEXJOxnftPwmk151TxC3eHvHX85scNZU/nBl3R
|
||||
VyNgumcjsSzfb3z2NzHlBH5YKOmGWnDRsnMhMf7qO3k2YI0/XiV+Yd4jHGWvAgMB
|
||||
AAGjUzBRMB0GA1UdDgQWBBS76NBkLueNtmDSt+kF9y3Q75VuUDAfBgNVHSMEGDAW
|
||||
gBS76NBkLueNtmDSt+kF9y3Q75VuUDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
|
||||
DQEBCwUAA4ICAQAhkFEXcDVFZmHtAJ3KY5t2OOFBKV1JRU2tbcV11qTbXjCPASz7
|
||||
AejV+rVqHYDnHMo/lJ0l+OvDl/UDU8wIQo/eBqbba96ClvSrEnEi7Ekx5sfPrLBP
|
||||
Q1Mg3aKv9PMtOME/QAc4JExjHBz2ZGffM9EcF7bv4hIqeGOCmEV6yXSYj3PDuLL+
|
||||
3F8LF39eKjT5TldrhAkk+sAsSdLLu14oqlbUW61XRrqZaU4PprYdYOGEjUlWTrOH
|
||||
L60Qy8EsqGzzohU7wljm0U1m90rGZGYdaoRVNPwuvX8Olb72BQZU8OtUlJbFCHIE
|
||||
nPpHSN9sKtS1SHme3nYo/dOcuHhjXwoBXiWESEVCsJCAwubCKpj8YlB2JQqHKNUc
|
||||
fLQoueR6gBrsAP7cXAdEUnXX91cLYbetq09DXquV/b/YDctHuhRJO1kHrollQaWe
|
||||
XCFHpy3wJjxzoOMDrp1wsVidG8ogTkWxAwW1zbH/9tIW8cbvuVvoUXnzCO3CPBU6
|
||||
uOuOFLNFl2JJcF0Uddsg62LEOJkiVwhqmBFPWU9ywQ5lFrWK13uZjcnpeP7KWt3+
|
||||
0NqsRLX3uwU6yBd3aNcmps4yUPAjKbHCXm54/VBjuv1GOoBFWGrI0B5RjuZ+PZUH
|
||||
lYRmOVpgA3EioOvMukDE/p96Her3MISxplyU0loUlgXBbTj7VI212FQefg==
|
||||
-----END CERTIFICATE-----
|
52
others/wildcard_key.pem
Normal file
52
others/wildcard_key.pem
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
SOS2HtRM0ZdpfQqThSDEKSQ0xkt6
|
||||
-----END PRIVATE KEY-----
|
65
pnpm-lock.yaml
generated
65
pnpm-lock.yaml
generated
@ -20,6 +20,7 @@ importers:
|
||||
'@fastify/cors': 8.1.0
|
||||
'@fastify/env': 4.1.0
|
||||
'@fastify/jwt': 6.3.2
|
||||
'@fastify/multipart': 7.2.0
|
||||
'@fastify/static': 6.5.0
|
||||
'@iarna/toml': 2.2.5
|
||||
'@ladjs/graceful': 3.0.2
|
||||
@ -60,6 +61,7 @@ importers:
|
||||
prettier: 2.7.1
|
||||
prisma: 4.3.1
|
||||
public-ip: 6.0.1
|
||||
pump: ^3.0.0
|
||||
rimraf: 3.0.2
|
||||
ssh-config: 4.1.6
|
||||
strip-ansi: 7.0.1
|
||||
@ -73,6 +75,7 @@ importers:
|
||||
'@fastify/cors': 8.1.0
|
||||
'@fastify/env': 4.1.0
|
||||
'@fastify/jwt': 6.3.2
|
||||
'@fastify/multipart': 7.2.0
|
||||
'@fastify/static': 6.5.0
|
||||
'@iarna/toml': 2.2.5
|
||||
'@ladjs/graceful': 3.0.2
|
||||
@ -102,6 +105,7 @@ importers:
|
||||
p-all: 4.0.0
|
||||
p-throttle: 5.0.0
|
||||
public-ip: 6.0.1
|
||||
pump: 3.0.0
|
||||
ssh-config: 4.1.6
|
||||
strip-ansi: 7.0.1
|
||||
unique-names-generator: 4.7.1
|
||||
@ -161,6 +165,7 @@ importers:
|
||||
prettier-plugin-svelte: 2.7.0
|
||||
svelte: 3.50.0
|
||||
svelte-check: 2.9.0
|
||||
svelte-file-dropzone: ^1.0.0
|
||||
svelte-preprocess: 4.10.7
|
||||
svelte-select: 4.4.7
|
||||
sveltekit-i18n: 2.2.2
|
||||
@ -177,6 +182,7 @@ importers:
|
||||
dayjs: 1.11.5
|
||||
js-cookie: 3.0.1
|
||||
p-limit: 4.0.0
|
||||
svelte-file-dropzone: 1.0.0
|
||||
svelte-select: 4.4.7
|
||||
sveltekit-i18n: 2.2.2_svelte@3.50.0
|
||||
devDependencies:
|
||||
@ -200,7 +206,7 @@ importers:
|
||||
svelte: 3.50.0
|
||||
svelte-check: 2.9.0_shxyscafa2tdzd4z2tgnnrhyyu
|
||||
svelte-preprocess: 4.10.7_gzukngxpmlbzkiu3cz7vpamp3y
|
||||
tailwindcss: 3.1.8
|
||||
tailwindcss: 3.1.8_postcss@8.4.16
|
||||
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.1.8
|
||||
tslib: 2.4.0
|
||||
typescript: 4.8.2
|
||||
@ -314,6 +320,13 @@ packages:
|
||||
pkg-up: 3.1.0
|
||||
dev: false
|
||||
|
||||
/@fastify/busboy/1.1.0:
|
||||
resolution: {integrity: sha512-Fv854f94v0CzIDllbY3i/0NJPNBRNLDawf3BTYVGCe9VrIIs3Wi7AFx24F9NzCxdf0wyx/x0Q9kEVnvDOPnlxA==}
|
||||
engines: {node: '>=10.17.0'}
|
||||
dependencies:
|
||||
text-decoding: 1.0.0
|
||||
dev: false
|
||||
|
||||
/@fastify/cookie/8.1.0:
|
||||
resolution: {integrity: sha512-+BxpyK4KLAjDpXdWxOjl8yaKtAoqYZR+CE9+cNtdMDoACb8hcpGx9npkrdINl62EpCu06oIPluq8A4NUsi78ZA==}
|
||||
dependencies:
|
||||
@ -328,6 +341,10 @@ packages:
|
||||
mnemonist: 0.39.2
|
||||
dev: false
|
||||
|
||||
/@fastify/deepmerge/1.1.0:
|
||||
resolution: {integrity: sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==}
|
||||
dev: false
|
||||
|
||||
/@fastify/env/4.1.0:
|
||||
resolution: {integrity: sha512-9l+JTUiFWSwb9dGSeR46aDWBjrAg8lJeqMjbotG5/8Ho90+qzRbt8kdSnVhLm5k6HcqXcBaBAT/6cImRhRq0VQ==}
|
||||
dependencies:
|
||||
@ -355,6 +372,19 @@ packages:
|
||||
steed: 1.1.3
|
||||
dev: false
|
||||
|
||||
/@fastify/multipart/7.2.0:
|
||||
resolution: {integrity: sha512-LwfwbCLy30Be1pa5q7F8xCTygxJdEWkfkQhD4OWZ13+vMH4tP/6Bu3OkSTlFatxLAmbEl2UpHLf7CU7w7csRIw==}
|
||||
dependencies:
|
||||
'@fastify/busboy': 1.1.0
|
||||
'@fastify/deepmerge': 1.1.0
|
||||
'@fastify/error': 3.0.0
|
||||
end-of-stream: 1.4.4
|
||||
fastify-plugin: 4.2.1
|
||||
hexoid: 1.0.0
|
||||
secure-json-parse: 2.4.0
|
||||
stream-wormhole: 1.1.0
|
||||
dev: false
|
||||
|
||||
/@fastify/static/6.5.0:
|
||||
resolution: {integrity: sha512-WEk6iqgejA6ivjkvbJ47A+uMci225z5lZwLXCXZS3ZYR/kYje1gzzarkKKGL6TWpBw6smkOzxA7dfEoY0347Nw==}
|
||||
dependencies:
|
||||
@ -584,7 +614,7 @@ packages:
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.merge: 4.6.2
|
||||
postcss-selector-parser: 6.0.10
|
||||
tailwindcss: 3.1.8
|
||||
tailwindcss: 3.1.8_postcss@8.4.16
|
||||
dev: false
|
||||
|
||||
/@tsconfig/node10/1.0.8:
|
||||
@ -2334,7 +2364,7 @@ packages:
|
||||
css-selector-tokenizer: 0.8.0
|
||||
postcss: 8.4.16
|
||||
postcss-js: 4.0.0_postcss@8.4.16
|
||||
tailwindcss: 3.1.8
|
||||
tailwindcss: 3.1.8_postcss@8.4.16
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
dev: false
|
||||
@ -3213,6 +3243,13 @@ packages:
|
||||
flat-cache: 3.0.4
|
||||
dev: true
|
||||
|
||||
/file-selector/0.2.4:
|
||||
resolution: {integrity: sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==}
|
||||
engines: {node: '>= 10'}
|
||||
dependencies:
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/fill-range/7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -5554,6 +5591,11 @@ packages:
|
||||
reusify: 1.0.4
|
||||
dev: false
|
||||
|
||||
/stream-wormhole/1.1.0:
|
||||
resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dev: false
|
||||
|
||||
/string-width/4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
@ -5728,6 +5770,12 @@ packages:
|
||||
- sugarss
|
||||
dev: true
|
||||
|
||||
/svelte-file-dropzone/1.0.0:
|
||||
resolution: {integrity: sha512-F2DN+wN2w7bKuUJFYQOFsdtTgaohQ/rNKau5m5n2l3LHJRRIccYS4wpq8f6dz/h5aSxYse3oRclmYdW6FWAfjw==}
|
||||
dependencies:
|
||||
file-selector: 0.2.4
|
||||
dev: false
|
||||
|
||||
/svelte-heros/2.3.5:
|
||||
resolution: {integrity: sha512-08PdccaeRPP1pVa90AGieTwGzrNtXpC1Fry+i95OTvcR3xbGRU/hxK4rnaFYvGgk1Pxj9YT6GKGTEX8uXE9XJQ==}
|
||||
dev: true
|
||||
@ -5816,13 +5864,15 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: '>= 2.x.x'
|
||||
dependencies:
|
||||
tailwindcss: 3.1.8
|
||||
tailwindcss: 3.1.8_postcss@8.4.16
|
||||
dev: true
|
||||
|
||||
/tailwindcss/3.1.8:
|
||||
/tailwindcss/3.1.8_postcss@8.4.16:
|
||||
resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
arg: 5.0.2
|
||||
chokidar: 3.5.3
|
||||
@ -5869,6 +5919,10 @@ packages:
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
|
||||
/text-decoding/1.0.0:
|
||||
resolution: {integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==}
|
||||
dev: false
|
||||
|
||||
/text-table/0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
dev: true
|
||||
@ -5986,7 +6040,6 @@ packages:
|
||||
|
||||
/tslib/2.4.0:
|
||||
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
|
||||
dev: true
|
||||
|
||||
/tsutils/3.21.0_typescript@4.8.2:
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
|
Loading…
x
Reference in New Issue
Block a user