This commit is contained in:
Andras Bacsai
2022-10-28 11:54:03 +02:00
parent aa27aeafa1
commit dc626bd4f0
20 changed files with 422 additions and 97 deletions

View File

@@ -2,7 +2,12 @@
export let type: string;
export let isAbsolute = true;
import * as Icons from '$lib/components/svg/services';
const name: any = type && type[0].toUpperCase() + type.substring(1).toLowerCase();
const name: any =
type &&
(type[0].toUpperCase() + type.substring(1).toLowerCase())
.replaceAll('.', '')
.replaceAll(' ', '')
.split('-')[0];
</script>
<svelte:component this={Icons[name.replace('.','').replaceAll(' ','')]} {isAbsolute} />
<svelte:component this={Icons[name]} {isAbsolute} />

View File

@@ -41,7 +41,7 @@
"saving": "Saving...",
"name": "Name",
"value": "Value",
"action": "Action",
"action": "Actions",
"is_required": "is required.",
"add": "Add",
"set": "Set",

View File

@@ -1,6 +1,7 @@
<script lang="ts">
export let name = '';
export let value = '';
export let readonly = false;
export let isNewSecret = false;
import { page } from '$app/stores';
@@ -55,19 +56,20 @@
bind:value={name}
required
placeholder="EXAMPLE_VARIABLE"
readonly={!isNewSecret}
readonly={!isNewSecret || readonly}
class="w-full"
class:bg-coolblack={!isNewSecret}
class:border={!isNewSecret}
class:border-dashed={!isNewSecret}
class:border-coolgray-300={!isNewSecret}
class:cursor-not-allowed={!isNewSecret}
/>
</td>
<td>
<CopyPasswordField
id={isNewSecret ? 'secretValue' : 'secretValueNew'}
name={isNewSecret ? 'secretValue' : 'secretValueNew'}
disabled={readonly}
{readonly}
isPasswordField={true}
bind:value
placeholder="J$#@UIO%HO#$U%H"
@@ -80,7 +82,7 @@
<div class="flex items-center justify-center">
<button class="btn btn-sm btn-primary" on:click={() => saveSecret(true)}>Add</button>
</div>
{:else}
{:else if !readonly}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="btn btn-sm btn-primary" on:click={() => saveSecret(false)}>Set</button>

View File

@@ -0,0 +1,86 @@
<script lang="ts">
import { post } from '$lib/api';
import { page } from '$app/stores';
import { status } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Setting from '$lib/components/Setting.svelte';
import { browser } from '$app/env';
import { errorNotification, getDomain } from '$lib/common';
export let service: any;
const { id } = $page.params;
const settings = service.settings;
const { ipv4, ipv6 } = settings;
let ftpUrl = generateUrl(service.wordpress?.ftpPublicPort) || '';
let ftpUser = service.wordpress?.ftpUser;
let ftpPassword = service.wordpress?.ftpPassword;
let ftpLoading = false;
let ftpEnabled = service.wordpress?.ftpEnabled || false;
function generateUrl(publicPort: any) {
return browser
? `sftp://${settings?.fqdn ? getDomain(settings.fqdn) : ipv4 || ipv6}:${publicPort}`
: 'Loading...';
}
async function changeSettings(name: any) {
if (ftpLoading) return;
if ($status.service.overallStatus === 'healthy') {
ftpLoading = true;
if (name === 'ftpEnabled') {
ftpEnabled = !ftpEnabled;
}
try {
const {
publicPort,
ftpUser: user,
ftpPassword: password
} = await post(`/services/${id}/wordpress/ftp`, {
ftpEnabled
});
ftpUrl = generateUrl(publicPort);
ftpUser = user;
ftpPassword = password;
service.wordpress.ftpEnabled = ftpEnabled;
} catch (error) {
return errorNotification(error);
} finally {
ftpLoading = false;
}
}
}
</script>
<div class="grid grid-cols-2 items-center">
<Setting
id="ftpEnabled"
bind:setting={ftpEnabled}
loading={ftpLoading}
disabled={$status.service.overallStatus !== 'healthy'}
on:click={() => changeSettings('ftpEnabled')}
title="Enable sFTP connection to WordPress data"
description="Enables an on-demand sFTP connection to the WordPress data directory. This is useful if you want to use sFTP to upload files."
/>
</div>
{#if service.wordpress?.ftpEnabled}
<div class="grid grid-cols-2 items-center">
<label for="ftpUrl">sFTP Connection URI</label>
<CopyPasswordField id="ftpUrl" readonly disabled name="ftpUrl" value={ftpUrl} />
</div>
<div class="grid grid-cols-2 items-center">
<label for="ftpUser">User</label>
<CopyPasswordField id="ftpUser" readonly disabled name="ftpUser" value={ftpUser} />
</div>
<div class="grid grid-cols-2 items-center">
<label for="ftpPassword">Password</label>
<CopyPasswordField
id="ftpPassword"
isPasswordField
readonly
disabled
name="ftpPassword"
value={ftpPassword}
/>
</div>
{/if}

View File

@@ -41,7 +41,7 @@
async function handleSubmit(service: any) {
try {
await post(`/services/${id}/configuration/type`, { type: service.name });
await post(`/services/${id}/configuration/type`, { type: service.type });
return await goto(from || `/services/${id}`);
} catch (error) {
return errorNotification(error);
@@ -50,8 +50,8 @@
function doSearch() {
filteredServices = services.filter(
(service: any) =>
service.name.toLowerCase().includes(search.toLowerCase()) ||
service.labels?.some((label: string) => label.toLowerCase().includes(search.toLowerCase()))
service.name.toLowerCase().includes(search.toLowerCase()) ||
service.labels?.some((label: string) => label.toLowerCase().includes(search.toLowerCase()))
);
}
function cleanupSearch() {
@@ -62,6 +62,7 @@
<div class="container lg:mx-auto lg:p-0 px-8 pt-5">
<div class="input-group flex w-full">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="btn btn-square cursor-default no-animation hover:bg-error" on:click={cleanupSearch}>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -91,15 +92,15 @@
<div class="container lg:mx-auto lg:pt-20 lg:p-0 px-8 pt-20">
<div class="flex flex-wrap justify-center gap-8">
{#each filteredServices as service}
{#key service.name}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(service)}>
<button type="submit" class="box-selection relative text-xl font-bold hover:bg-primary">
<ServiceIcons type={service.name} />
{service.name}
</button>
</form>
</div>
{#key service.name}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(service)}>
<button type="submit" class="box-selection relative text-xl font-bold hover:bg-primary">
<ServiceIcons type={service.type} />
{service.name}
</button>
</form>
</div>
{/key}
{/each}
</div>

View File

@@ -35,6 +35,7 @@
import ServiceStatus from '$lib/components/ServiceStatus.svelte';
import { saveForm } from './utils';
import Select from 'svelte-select';
import Wordpress from './_Services/wordpress.svelte';
const { id } = $page.params;
$: isDisabled =
@@ -86,7 +87,7 @@
exposePort: service.exposePort
});
for (const setting of service.serviceSetting) {
if (setting.variableName.startsWith('$$coolify_fqdn') && setting.value) {
if (setting.variableName?.startsWith('$$coolify_fqdn') && setting.value) {
for (let field of formData) {
const [key, value] = field;
if (setting.name === key) {
@@ -413,7 +414,6 @@
{template[oneService].name ||
oneService.replace(`${id}-`, '').replace(id, service.type)}
</div>
<ServiceStatus id={oneService} />
</div>
<div class="grid grid-flow-row gap-2 px-4">
@@ -434,6 +434,8 @@
name={variable.name}
id={variable.name}
value={service.fqdn}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.defaultValue === '$$generate_domain'}
<CopyPasswordField
@@ -442,6 +444,8 @@
name={variable.name}
id={variable.name}
value={getDomain(service.fqdn) || ''}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.defaultValue === '$$generate_network'}
<CopyPasswordField
@@ -450,6 +454,8 @@
name={variable.name}
id={variable.name}
value={service.destinationDocker.network}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.defaultValue === 'true' || variable.defaultValue === 'false'}
{#if variable.value === 'true' || variable.value === 'false'}
@@ -461,6 +467,8 @@
name={variable.name}
bind:value={variable.value}
form="saveForm"
placeholder={variable.placeholder}
required={variable?.required}
>
<option value="true">enabled</option>
<option value="false">disabled</option>
@@ -474,6 +482,8 @@
name={variable.name}
bind:value={variable.defaultValue}
form="saveForm"
placeholder={variable.placeholder}
required={variable?.required}
>
<option value="true">true</option>
<option value="false"> false</option>
@@ -487,21 +497,40 @@
name={variable.name}
id={variable.name}
value={variable.value}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else if variable.type === 'textarea'}
<textarea
class="w-full"
value={variable.value}
readonly={isDisabled}
disabled={isDisabled}
class:resize-none={$status.service.overallStatus === 'healthy'}
rows="5"
name={variable.name}
id={variable.name}
placeholder={variable.placeholder}
required={variable?.required}
/>
{:else}
<CopyPasswordField
placeholder={variable.defaultValue || 'optional'}
isPasswordField={variable.id.startsWith('secret')}
required={variable?.required}
readonly={isDisabled}
disabled={isDisabled}
readonly={variable.readonly || isDisabled}
disabled={variable.readonly || isDisabled}
name={variable.name}
id={variable.name}
value={variable.value}
placeholder={variable.placeholder}
/>
{/if}
</div>
{/if}
{/each}
{#if template[oneService].name.toLowerCase() === 'wordpress' && service.type.startsWith('wordpress')}
<Wordpress {service} />
{/if}
{/if}
</div>
{/each}

View File

@@ -20,7 +20,6 @@
<script lang="ts">
export let secrets: any;
export let service: any;
import Secret from './_Secret.svelte';
import { page } from '$app/stores';
import { get } from '$lib/api';
@@ -84,7 +83,7 @@
{#each secrets as secret}
{#key secret.id}
<tr>
<Secret name={secret.name} value={secret.value} on:refresh={refreshSecrets} />
<Secret name={secret.name} value={secret.value} readonly={secret.readonly} on:refresh={refreshSecrets} />
</tr>
{/key}
{/each}

View File

@@ -43,27 +43,36 @@ export async function saveSecret({
export async function saveForm(formData: any, service: any) {
const settings = service.serviceSetting.map((setting: { name: string }) => setting.name);
const secrets = service.serviceSecret.map((secret: { name: string }) => secret.name);
const baseCoolifySetting = ['name', 'fqdn', 'exposePort', 'version'];
for (let field of formData) {
const [key, value] = field;
service.serviceSetting = service.serviceSetting.map((setting: any) => {
if (setting.name === key) {
setting.changed = true;
setting.value = value;
}
return setting;
});
if (!settings.includes(key) && !baseCoolifySetting.includes(key)) {
service.serviceSetting.push({
id: service.id,
if (secrets.includes(key)) {
await post(`/services/${service.id}/secrets`, {
name: key,
value: value,
isNew: true
value,
});
} else {
service.serviceSetting = service.serviceSetting.map((setting: any) => {
if (setting.name === key) {
setting.changed = true;
setting.value = value;
}
return setting;
});
if (!settings.includes(key) && !baseCoolifySetting.includes(key)) {
service.serviceSetting.push({
id: service.id,
name: key,
value: value,
isNew: true
});
}
if (baseCoolifySetting.includes(key)) {
service[key] = value;
}
}
if (baseCoolifySetting.includes(key)) {
service[key] = value;
}
}
await post(`/services/${service.id}`, { ...service });
const { service: reloadedService } = await get(`/services/${service.id}`);