mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-28 21:02:05 +00:00
tons of updates
This commit is contained in:
@@ -8,64 +8,410 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import Services from './_Services/_Services.svelte';
|
||||
import { get } from '$lib/api';
|
||||
import { page } from '$app/stores';
|
||||
import { status } from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import ServiceLinks from './_ServiceLinks.svelte';
|
||||
|
||||
export let service: any;
|
||||
export let readOnly: any;
|
||||
export let settings: any;
|
||||
|
||||
const { id } = $page.params;
|
||||
let loading = {
|
||||
usage: false
|
||||
};
|
||||
let usage = {
|
||||
MemUsage: 0,
|
||||
CPUPerc: 0,
|
||||
NetIO: 0
|
||||
};
|
||||
let usageInterval: any;
|
||||
import cuid from 'cuid';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
async function getUsage() {
|
||||
if (loading.usage) return;
|
||||
if (!$status.service.isRunning) return;
|
||||
loading.usage = true;
|
||||
const data = await get(`/services/${id}/usage`);
|
||||
usage = data.usage;
|
||||
loading.usage = false;
|
||||
import { browser } from '$app/env';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import { get, post } from '$lib/api';
|
||||
import { errorNotification, getDomain } from '$lib/common';
|
||||
import { t } from '$lib/translations';
|
||||
import {
|
||||
appSession,
|
||||
status,
|
||||
setLocation,
|
||||
addToast,
|
||||
checkIfDeploymentEnabledServices,
|
||||
isDeploymentEnabled
|
||||
} from '$lib/store';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import * as Services from '$lib/components/Services';
|
||||
|
||||
import DocLink from '$lib/components/DocLink.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
|
||||
const { id } = $page.params;
|
||||
let serviceName: any = service.type && service.type[0].toUpperCase() + service.type.substring(1);
|
||||
$: isDisabled =
|
||||
!$appSession.isAdmin || $status.service.isRunning || $status.service.initialLoading;
|
||||
|
||||
let forceSave = false;
|
||||
let loading = {
|
||||
save: false,
|
||||
verification: false,
|
||||
cleanup: false
|
||||
};
|
||||
let dualCerts = service.dualCerts;
|
||||
|
||||
let nonWWWDomain = service.fqdn && getDomain(service.fqdn).replace(/^www\./, '');
|
||||
let isNonWWWDomainOK = false;
|
||||
let isWWWDomainOK = false;
|
||||
|
||||
async function isDNSValid(domain: any, isWWW: any) {
|
||||
try {
|
||||
await get(`/services/${id}/check?domain=${domain}`);
|
||||
addToast({
|
||||
message: 'DNS configuration is valid.',
|
||||
type: 'success'
|
||||
});
|
||||
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
|
||||
return true;
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
isWWW ? (isWWWDomainOK = false) : (isNonWWWDomainOK = false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(usageInterval);
|
||||
});
|
||||
async function handleSubmit() {
|
||||
if (loading.save) return;
|
||||
loading.save = true;
|
||||
try {
|
||||
await post(`/services/${id}/check`, {
|
||||
fqdn: service.fqdn,
|
||||
forceSave,
|
||||
dualCerts,
|
||||
otherFqdns: service.minio?.apiFqdn ? [service.minio?.apiFqdn] : [],
|
||||
exposePort: service.exposePort
|
||||
});
|
||||
await post(`/services/${id}`, { ...service });
|
||||
setLocation(service);
|
||||
forceSave = false;
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||
return addToast({
|
||||
message: 'Configuration saved.',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
if (error?.message.startsWith($t('application.dns_not_set_partial_error'))) {
|
||||
forceSave = true;
|
||||
if (dualCerts) {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
const isWWW = getDomain(service.fqdn).includes('www.');
|
||||
if (isWWW) {
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
}
|
||||
async function setEmailsToVerified() {
|
||||
loading.verification = true;
|
||||
try {
|
||||
await post(`/services/${id}/${service.type}/activate`, { id: service.id });
|
||||
return addToast({
|
||||
message: t.get('services.all_email_verified'),
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.verification = false;
|
||||
}
|
||||
}
|
||||
async function migrateAppwriteDB() {
|
||||
loading.verification = true;
|
||||
try {
|
||||
await post(`/services/${id}/${service.type}/migrate`, { id: service.id });
|
||||
return addToast({
|
||||
message: "Appwrite's database has been migrated.",
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.verification = false;
|
||||
}
|
||||
}
|
||||
async function changeSettings(name: any) {
|
||||
try {
|
||||
if (name === 'dualCerts') {
|
||||
dualCerts = !dualCerts;
|
||||
}
|
||||
await post(`/services/${id}/settings`, { dualCerts });
|
||||
return addToast({
|
||||
message: t.get('application.settings_saved'),
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function cleanupLogs() {
|
||||
loading.cleanup = true;
|
||||
try {
|
||||
await post(`/services/${id}/${service.type}/cleanup`, { id: service.id });
|
||||
return addToast({
|
||||
message: 'Cleared DB Logs',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.cleanup = false;
|
||||
}
|
||||
}
|
||||
function doNothing() {
|
||||
return;
|
||||
}
|
||||
onMount(async () => {
|
||||
await getUsage();
|
||||
usageInterval = setInterval(async () => {
|
||||
await getUsage();
|
||||
}, 1000);
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
if (service.type === 'wordpress') {
|
||||
service.wordpress.mysqlDatabase = 'db';
|
||||
}
|
||||
if (service.type === 'plausibleanalytics') {
|
||||
service.plausibleAnalytics.email = 'noreply@demo.com';
|
||||
service.plausibleAnalytics.username = 'admin';
|
||||
}
|
||||
if (service.type === 'minio') {
|
||||
service.minio.apiFqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
if (service.type === 'ghost') {
|
||||
service.ghost.mariadbDatabase = 'db';
|
||||
}
|
||||
if (service.type === 'fider') {
|
||||
service.fider.emailNoreply = 'noreply@demo.com';
|
||||
}
|
||||
await handleSubmit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-6xl px-6 lg:my-0 my-4 lg:pt-0 pt-4 rounded">
|
||||
<div class="text-center">
|
||||
<div class="stat w-64">
|
||||
<div class="stat-title">Used Memory / Memory Limit</div>
|
||||
<div class="stat-value text-xl">{usage?.MemUsage}</div>
|
||||
<div class="w-full">
|
||||
<form on:submit|preventDefault={() => handleSubmit()}>
|
||||
<div class="mx-auto w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
|
||||
<div class="title font-bold pb-3 ">General</div>
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-sm"
|
||||
class:bg-orange-600={forceSave}
|
||||
class:hover:bg-orange-400={forceSave}
|
||||
class:loading={loading.save}
|
||||
class:btn-primary={!loading.save}
|
||||
disabled={loading.save}
|
||||
>{loading.save
|
||||
? $t('forms.save')
|
||||
: forceSave
|
||||
? $t('forms.confirm_continue')
|
||||
: $t('forms.save')}</button
|
||||
>
|
||||
{/if}
|
||||
{#if service.type === 'plausibleanalytics' && $status.service.isRunning}
|
||||
<div class="btn-group">
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
on:click|preventDefault={setEmailsToVerified}
|
||||
disabled={loading.verification}
|
||||
class:loading={loading.verification}
|
||||
>{loading.verification
|
||||
? $t('forms.verifying')
|
||||
: $t('forms.verify_emails_without_smtp')}</button
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
on:click|preventDefault={cleanupLogs}
|
||||
disabled={loading.cleanup}
|
||||
class:loading={loading.cleanup}>Cleanup Unnecessary Database Logs</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if service.type === 'appwrite' && $status.service.isRunning}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
on:click|preventDefault={migrateAppwriteDB}
|
||||
disabled={loading.verification}
|
||||
class:loading={loading.verification}
|
||||
>{loading.verification
|
||||
? 'Migrating... it may take a while...'
|
||||
: "Migrate Appwrite's Database"}</button
|
||||
>
|
||||
<DocLink url="https://appwrite.io/docs/upgrade#run-the-migration" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat w-64">
|
||||
<div class="stat-title">Used CPU</div>
|
||||
<div class="stat-value text-xl">{usage?.CPUPerc}</div>
|
||||
</div>
|
||||
{#if service.type === 'minio' && !service.minio.apiFqdn && $status.service.isRunning}
|
||||
<div class="py-5">
|
||||
<span class="font-bold text-red-500">IMPORTANT!</span> There was a small modification with Minio
|
||||
in the latest version of Coolify. Now you can separate the Console URL from the API URL, so you
|
||||
could use both through SSL. But this proccess cannot be done automatically, so you have to stop
|
||||
your Minio instance, configure the new domain and start it back. Sorry for any inconvenience.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="stat w-64">
|
||||
<div class="stat-title">Network IO</div>
|
||||
<div class="stat-value text-xl">{usage?.NetIO}</div>
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
<div class="mt-2 grid grid-cols-2 items-center">
|
||||
<label for="name">{$t('forms.name')}</label>
|
||||
<input name="name" id="name" class="w-full" bind:value={service.name} required />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="version">Version / Tag</label>
|
||||
<a
|
||||
href={$appSession.isAdmin && !$status.service.isRunning && !$status.service.initialLoading
|
||||
? `/services/${id}/configuration/version?from=/services/${id}`
|
||||
: ''}
|
||||
class="no-underline"
|
||||
>
|
||||
<input
|
||||
class="w-full"
|
||||
value={service.version}
|
||||
id="service"
|
||||
readonly
|
||||
disabled={$status.service.isRunning || $status.service.initialLoading}
|
||||
class:cursor-pointer={!$status.service.isRunning}
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="destination">{$t('application.destination')}</label>
|
||||
<div>
|
||||
{#if service.destinationDockerId}
|
||||
<div class="no-underline">
|
||||
<input
|
||||
value={service.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
class="bg-transparent w-full"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if service.type === 'minio'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="fqdn"
|
||||
>Console URL <Explainer explanation={$t('application.https_explainer')} /></label
|
||||
>
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://console.min.io"
|
||||
readonly={isDisabled}
|
||||
disabled={isDisabled}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="apiFqdn"
|
||||
>API URL <Explainer explanation={$t('application.https_explainer')} /></label
|
||||
>
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://min.io"
|
||||
readonly={!$appSession.isAdmin && !$status.service.isRunning}
|
||||
disabled={isDisabled}
|
||||
name="apiFqdn"
|
||||
id="apiFqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.minio.apiFqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="fqdn"
|
||||
>{$t('application.url_fqdn')}
|
||||
<Explainer explanation={$t('application.https_explainer')} />
|
||||
</label>
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://analytics.coollabs.io"
|
||||
readonly={!$appSession.isAdmin && !$status.service.isRunning}
|
||||
disabled={!$appSession.isAdmin ||
|
||||
$status.service.isRunning ||
|
||||
$status.service.initialLoading}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if forceSave}
|
||||
<div class="flex-col space-y-2 pt-4 text-center">
|
||||
{#if isNonWWWDomainOK}
|
||||
<button
|
||||
class="btn btn-sm bg-green-600 hover:bg-green-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm bg-red-600 hover:bg-red-500"
|
||||
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-green-600 hover:bg-green-500"
|
||||
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-red-600 hover:bg-red-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="dualCerts"
|
||||
disabled={$status.service.isRunning}
|
||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
description={$t('services.generate_www_non_www_ssl')}
|
||||
on:click={() => !$status.service.isRunning && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort"
|
||||
>Exposed Port <Explainer
|
||||
explanation={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="w-full"
|
||||
readonly={!$appSession.isAdmin && !$status.service.isRunning}
|
||||
disabled={!$appSession.isAdmin ||
|
||||
$status.service.isRunning ||
|
||||
$status.service.initialLoading}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={service.exposePort}
|
||||
placeholder="12345"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:component this={Services[serviceName]} bind:service {readOnly} {settings} />
|
||||
</form>
|
||||
</div>
|
||||
<Services bind:service bind:readOnly bind:settings />
|
||||
|
||||
Reference in New Issue
Block a user