lots of changes
This commit is contained in:
parent
9afb713df1
commit
a6f457749b
@ -0,0 +1,19 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_ServicePersistentStorage" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"path" TEXT NOT NULL,
|
||||
"volumeName" TEXT,
|
||||
"predefined" BOOLEAN NOT NULL DEFAULT false,
|
||||
"containerId" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_ServicePersistentStorage" ("createdAt", "id", "path", "serviceId", "updatedAt") SELECT "createdAt", "id", "path", "serviceId", "updatedAt" FROM "ServicePersistentStorage";
|
||||
DROP TABLE "ServicePersistentStorage";
|
||||
ALTER TABLE "new_ServicePersistentStorage" RENAME TO "ServicePersistentStorage";
|
||||
CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
@ -193,12 +193,15 @@ model ApplicationPersistentStorage {
|
||||
}
|
||||
|
||||
model ServicePersistentStorage {
|
||||
id String @id @default(cuid())
|
||||
serviceId String
|
||||
path String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
id String @id @default(cuid())
|
||||
serviceId String
|
||||
path String
|
||||
volumeName String?
|
||||
predefined Boolean @default(false)
|
||||
containerId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
|
||||
@@unique([serviceId, path])
|
||||
}
|
||||
@ -419,13 +422,13 @@ model Service {
|
||||
}
|
||||
|
||||
model ServiceSetting {
|
||||
id String @id @default(cuid())
|
||||
serviceId String
|
||||
name String
|
||||
value String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
id String @id @default(cuid())
|
||||
serviceId String
|
||||
name String
|
||||
value String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
|
||||
@@unique([serviceId, name])
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { defaultServiceConfigurations } from '../services';
|
||||
import { OnlyId } from '../../types';
|
||||
import templates from '../templates'
|
||||
import { parseAndFindServiceTemplates } from '../../routes/api/v1/services/handlers';
|
||||
|
||||
import path from 'path';
|
||||
// export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
// try {
|
||||
// const { type } = request.params
|
||||
@ -692,7 +692,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
const teamId = request.user.teamId;
|
||||
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret,serviceSetting, exposePort, persistentStorage } =
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, serviceSetting, exposePort, persistentStorage } =
|
||||
service;
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
@ -701,9 +701,10 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
|
||||
const config = {};
|
||||
for (const service in template.services) {
|
||||
console.log(template.services[service])
|
||||
config[service] = {
|
||||
container_name: service,
|
||||
build: template.services[service].build || undefined,
|
||||
command: template.services[service].command,
|
||||
image: template.services[service].image,
|
||||
expose: template.services[service].ports,
|
||||
// ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
@ -714,16 +715,23 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
labels: makeLabelForServices(type),
|
||||
...defaultComposeConfiguration(network),
|
||||
}
|
||||
|
||||
|
||||
// Generate files for builds
|
||||
if (template.services[service].build) {
|
||||
if (template.services[service]?.extras?.files?.length > 0) {
|
||||
console.log(template.services[service]?.extras?.files)
|
||||
let Dockerfile = `
|
||||
FROM ${template.services[service].image}`
|
||||
for (const file of template.services[service].extras.files) {
|
||||
const { source, destination, content } = file;
|
||||
await fs.writeFile(source, content);
|
||||
Dockerfile += `
|
||||
COPY ./${path.basename(source)} ${destination}`
|
||||
}
|
||||
await fs.writeFile(`${workdir}/Dockerfile.${service}`, Dockerfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: config,
|
||||
|
@ -43,7 +43,7 @@ export default [
|
||||
"$$id": {
|
||||
"name": "Plausible Analytics",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"command": ['sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"'],
|
||||
"command": 'sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"',
|
||||
"depends_on": [
|
||||
"$$id-postgresql",
|
||||
"$$id-clickhouse"
|
||||
@ -52,7 +52,7 @@ export default [
|
||||
"environment": [
|
||||
"ADMIN_USER_EMAIL=$$config_admin_user_email",
|
||||
"ADMIN_USER_NAME=$$config_admin_user_name",
|
||||
"ADMIN_USER_PASSWORD=$$secret_admin_user_password",
|
||||
"ADMIN_USER_PWD=$$secret_admin_user_pwd",
|
||||
"BASE_URL=$$config_base_url",
|
||||
"SECRET_KEY_BASE=$$secret_secret_key_base",
|
||||
"DISABLE_AUTH=$$config_disable_auth",
|
||||
@ -68,6 +68,9 @@ export default [
|
||||
"name": "PostgreSQL",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"image": "bitnami/postgresql:13.2.0",
|
||||
"volumes": [
|
||||
'$$id-postgresql-data:/bitnami/postgresql/',
|
||||
],
|
||||
"environment": [
|
||||
"POSTGRESQL_PASSWORD=$$secret_postgresql_password",
|
||||
"POSTGRESQL_USERNAME=$$config_postgresql_username",
|
||||
@ -78,7 +81,13 @@ export default [
|
||||
"$$id-clickhouse": {
|
||||
"name": "Clickhouse",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"build": "$$workdir",
|
||||
"build": {
|
||||
context: "$$workdir",
|
||||
dockerfile: "Dockerfile.$$id-clickhouse"
|
||||
},
|
||||
"volumes": [
|
||||
'$$id-clickhouse-data:/var/lib/clickhouse',
|
||||
],
|
||||
"image": "yandex/clickhouse-server:21.3.2.5",
|
||||
"ulimits": {
|
||||
"nofile": {
|
||||
@ -87,21 +96,25 @@ export default [
|
||||
}
|
||||
},
|
||||
"extras": {
|
||||
"files:": [
|
||||
"files": [
|
||||
{
|
||||
location: '$$workdir/clickhouse-config.xml',
|
||||
source: "$$workdir/clickhouse-config.xml",
|
||||
destination: '/etc/clickhouse-server/users.d/logging.xml',
|
||||
content: '<yandex><logger><level>warning</level><console>true</console></logger><query_thread_log remove="remove"/><query_log remove="remove"/><text_log remove="remove"/><trace_log remove="remove"/><metric_log remove="remove"/><asynchronous_metric_log remove="remove"/><session_log remove="remove"/><part_log remove="remove"/></yandex>'
|
||||
},
|
||||
{
|
||||
location: '$$workdir/clickhouse-user-config.xml',
|
||||
source: "$$workdir/clickhouse-user-config.xml",
|
||||
destination: '/etc/clickhouse-server/config.d/logging.xml',
|
||||
content: '<yandex><profiles><default><log_queries>0</log_queries><log_query_threads>0</log_query_threads></default></profiles></yandex>'
|
||||
},
|
||||
{
|
||||
location: '$$workdir/init.query',
|
||||
source: "$$workdir/init.query",
|
||||
destination: '/docker-entrypoint-initdb.d/init.query',
|
||||
content: 'CREATE DATABASE IF NOT EXISTS plausible;'
|
||||
},
|
||||
{
|
||||
location: '$$workdir/init-db.sh',
|
||||
source: "$$workdir/init-db.sh",
|
||||
destination: '/docker-entrypoint-initdb.d/init-db.sh',
|
||||
content: 'clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query'
|
||||
}
|
||||
]
|
||||
@ -146,12 +159,14 @@ export default [
|
||||
"description": "This is the admin username. Please change it.",
|
||||
},
|
||||
{
|
||||
"id": "$$secret_admin_user_password",
|
||||
"name": "ADMIN_USER_PASSWORD",
|
||||
"showAsConfiguration": true,
|
||||
"id": "$$secret_admin_user_pwd",
|
||||
"name": "ADMIN_USER_PWD",
|
||||
"label": "Admin User Password",
|
||||
"defaultValue": "$$generate_password",
|
||||
"description": "This is the admin password. Please change it.",
|
||||
"extras": {
|
||||
"isVisibleOnUI": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "$$secret_secret_key_base",
|
||||
@ -159,8 +174,8 @@ export default [
|
||||
"label": "Secret Key Base",
|
||||
"defaultValue": "$$generate_passphrase",
|
||||
"description": "",
|
||||
"details": {
|
||||
"length":64
|
||||
"extras": {
|
||||
"length": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -132,8 +132,8 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
|
||||
const label = foundTemplate.variables.find(v => v.name === envKey)?.label
|
||||
const description = foundTemplate.variables.find(v => v.name === envKey)?.description
|
||||
const defaultValue = foundTemplate.variables.find(v => v.name === envKey)?.defaultValue
|
||||
const showAsConfiguration = foundTemplate.variables.find(v => v.name === envKey)?.showAsConfiguration
|
||||
if (envValue.startsWith('$$config') || showAsConfiguration) {
|
||||
const isVisibleOnUI = foundTemplate.variables.find(v => v.name === envKey)?.extras?.isVisibleOnUI
|
||||
if (envValue.startsWith('$$config') || isVisibleOnUI) {
|
||||
parsedTemplate[realKey].environment.push(
|
||||
{ name: envKey, value: envValue, label, description, defaultValue }
|
||||
)
|
||||
@ -220,12 +220,11 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
|
||||
foundTemplate.variables = foundTemplate.variables.map(variable => {
|
||||
let { id: variableId } = variable;
|
||||
if (variableId.startsWith('$$secret_')) {
|
||||
const length = variable?.extras && variable.extras['length']
|
||||
if (variable.defaultValue === '$$generate_password') {
|
||||
const length = variable?.details['length'] || null
|
||||
variable.value = generatePassword({length});
|
||||
variable.value = generatePassword({ length });
|
||||
} else if (variable.defaultValue === '$$generate_passphrase') {
|
||||
const length = variable?.details['length'] || null
|
||||
variable.value = generatePassword({length});
|
||||
variable.value = generatePassword({ length });
|
||||
}
|
||||
}
|
||||
if (variableId.startsWith('$$config_')) {
|
||||
@ -267,6 +266,18 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const service of Object.keys(foundTemplate.services)) {
|
||||
if (foundTemplate.services[service].volumes) {
|
||||
for (const volume of foundTemplate.services[service].volumes) {
|
||||
const [volumeName, path] = volume.split(':')
|
||||
if (!volumeName.startsWith('/')) {
|
||||
await prisma.servicePersistentStorage.create({
|
||||
data: { volumeName, path, containerId: service, predefined: true, service: { connect: { id } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await prisma.service.update({ where: { id }, data: { type, version: foundTemplate.serviceDefaultVersion } })
|
||||
return reply.code(201).send()
|
||||
} else {
|
||||
|
@ -329,10 +329,9 @@ export async function traefikConfiguration(request, reply) {
|
||||
fqdn,
|
||||
id,
|
||||
type,
|
||||
destinationDocker,
|
||||
destinationDockerId,
|
||||
dualCerts,
|
||||
plausibleAnalytics
|
||||
serviceSetting
|
||||
} = service;
|
||||
if (destinationDockerId) {
|
||||
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||
@ -348,8 +347,11 @@ export async function traefikConfiguration(request, reply) {
|
||||
if (isRunning) {
|
||||
// Plausible Analytics custom script
|
||||
let scriptName = false;
|
||||
if (type === 'plausibleanalytics' && plausibleAnalytics.scriptName !== 'plausible.js') {
|
||||
scriptName = plausibleAnalytics.scriptName;
|
||||
if (type === 'plausibleanalytics') {
|
||||
const foundScriptName = serviceSetting.find((a) => a.name === 'SCRIPT_NAME')?.value;
|
||||
if (foundScriptName) {
|
||||
scriptName = foundScriptName;
|
||||
}
|
||||
}
|
||||
|
||||
let container = id;
|
||||
|
@ -8,7 +8,7 @@
|
||||
export let setting: any;
|
||||
export let title: any;
|
||||
export let isBeta: any = false;
|
||||
export let description: any;
|
||||
export let description: any = null;
|
||||
export let isCenter = true;
|
||||
export let disabled = false;
|
||||
export let dataTooltip: any = null;
|
||||
|
@ -59,18 +59,33 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full grid gap-2">
|
||||
<div class="flex flex-col pb-2">
|
||||
<div class="flex flex-col lg:flex-row lg:space-y-0 space-y-2">
|
||||
<div class="w-full lg:px-0 px-4">
|
||||
<div class="grid grid-col-1 lg:grid-cols-3 lg:space-x-4" class:pt-8={isNew}>
|
||||
{#if storage.id}
|
||||
<div class="flex flex-col">
|
||||
<label for="name" class="pb-2 uppercase font-bold">Volume name</label>
|
||||
<input
|
||||
disabled
|
||||
readonly
|
||||
class="w-full lg:w-64"
|
||||
value="{storage.id}{storage.path.replace(/\//gi, '-')}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-col">
|
||||
<label for="name" class="pb-2 uppercase font-bold">{isNew ? 'New Path' : 'Path'}</label>
|
||||
<input
|
||||
class="w-full lg:w-64"
|
||||
bind:value={storage.path}
|
||||
required
|
||||
placeholder="eg: /sqlite.db"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pt-8">
|
||||
{#if isNew}
|
||||
<div class="flex items-center justify-center w-full lg:w-64">
|
||||
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(true)}
|
||||
<button class="btn btn-sm btn-primary w-full" on:click={() => saveStorage(true)}
|
||||
>{$t('forms.add')}</button
|
||||
>
|
||||
</div>
|
||||
|
@ -42,7 +42,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="name" class="pb-2 uppercase font-bold">name</label>
|
||||
|
||||
{#each persistentStorages as storage}
|
||||
{#key storage.id}
|
||||
<Storage on:refresh={refreshStorage} {storage} />
|
||||
|
@ -59,35 +59,76 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-fullgrid gap-2">
|
||||
<div class="flex flex-col pb-2">
|
||||
<div class="flex flex-col lg:flex-row lg:space-y-0 space-y-2">
|
||||
<input
|
||||
class="w-full lg:w-64"
|
||||
bind:value={storage.path}
|
||||
required
|
||||
placeholder="eg: /sqlite.db"
|
||||
/>
|
||||
{#if isNew}
|
||||
<div class="flex items-center justify-center w-full lg:w-64">
|
||||
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(true)}
|
||||
>{$t('forms.add')}</button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-64">
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(false)}
|
||||
>{$t('forms.set')}</button
|
||||
>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button class="btn btn-sm btn-error" on:click={removeStorage}
|
||||
>{$t('forms.remove')}</button
|
||||
>
|
||||
</div>
|
||||
<div class="w-full lg:px-0 px-4">
|
||||
{#if storage.predefined}
|
||||
<div class="grid grid-col-1 lg:grid-cols-2 pt-2">
|
||||
<div>
|
||||
<input
|
||||
id={storage.id}
|
||||
disabled
|
||||
readonly
|
||||
class="w-full"
|
||||
value={`${storage.id}${storage.path.replace(/\//gi, '-')}:${storage.path}`}
|
||||
/>
|
||||
</div>
|
||||
<div class="lg:px-2">
|
||||
<input
|
||||
id={storage.containerId}
|
||||
disabled
|
||||
readonly
|
||||
class="w-full"
|
||||
value={`${storage.containerId}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid grid-col-1 lg:grid-cols-3 lg:space-x-4" class:pt-8={isNew}>
|
||||
{#if storage.id}
|
||||
<div class="flex flex-col">
|
||||
<input
|
||||
disabled
|
||||
readonly
|
||||
class="w-full"
|
||||
value="{storage.id}{storage.path.replace(/\//gi, '-')}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-col">
|
||||
{#if isNew}
|
||||
<label for="name" class="pb-2 uppercase font-bold">Path</label>
|
||||
{/if}
|
||||
<input
|
||||
disabled={storage.predefined}
|
||||
readonly={storage.predefined}
|
||||
class="w-full lg:w-64"
|
||||
bind:value={storage.path}
|
||||
required
|
||||
placeholder="eg: /sqlite.db"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class:pt-8={isNew} class:pt-2={!isNew}>
|
||||
{#if isNew}
|
||||
<div class="flex items-center justify-center w-full lg:w-64">
|
||||
<button class="btn btn-sm btn-primary w-full" on:click={() => saveStorage(true)}
|
||||
>{$t('forms.add')}</button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-64">
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(false)}
|
||||
>{$t('forms.set')}</button
|
||||
>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button class="btn btn-sm btn-error" on:click={removeStorage}
|
||||
>{$t('forms.remove')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -252,27 +252,6 @@
|
||||
Loading...
|
||||
</button>
|
||||
{:else if $status.service.overallStatus === 'healthy'}
|
||||
<button
|
||||
on:click={stopService}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm btn-error gap-2"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 "
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||
</svg> Stop
|
||||
</button>
|
||||
<button
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
@ -297,6 +276,27 @@
|
||||
|
||||
Force Redeploy
|
||||
</button>
|
||||
<button
|
||||
on:click={stopService}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm btn-error gap-2"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 "
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="5" y="5" width="14" height="14" rx="2" />
|
||||
</svg>
|
||||
Stop
|
||||
</button>
|
||||
{:else if $status.service.overallStatus === 'degraded'}
|
||||
<button
|
||||
on:click={stopService}
|
||||
|
@ -19,6 +19,7 @@
|
||||
import { get, post } from '$lib/api';
|
||||
import { errorNotification, getDomain } from '$lib/common';
|
||||
import { t } from '$lib/translations';
|
||||
import Select from 'svelte-select';
|
||||
import {
|
||||
appSession,
|
||||
status,
|
||||
@ -29,18 +30,18 @@
|
||||
} 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';
|
||||
import ServiceStatus from '$lib/components/ServiceStatus.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;
|
||||
!$appSession.isAdmin ||
|
||||
$status.service.overallStatus === 'degraded' ||
|
||||
$status.service.overallStatus === 'healthy' ||
|
||||
$status.service.initialLoading;
|
||||
|
||||
let newConfiguration = null;
|
||||
let forceSave = false;
|
||||
let loading = {
|
||||
save: false,
|
||||
@ -73,12 +74,13 @@
|
||||
const formData = new FormData(e.target);
|
||||
for (let field of formData) {
|
||||
const [key, value] = field;
|
||||
for (const setting of service.serviceSetting) {
|
||||
if (setting.name === key && setting.value !== value) {
|
||||
service.serviceSetting = service.serviceSetting.map((setting: any) => {
|
||||
if (setting.name === key) {
|
||||
setting.changed = true;
|
||||
setting.value = value;
|
||||
}
|
||||
}
|
||||
return setting;
|
||||
});
|
||||
}
|
||||
if (loading.save) return;
|
||||
loading.save = true;
|
||||
@ -203,7 +205,7 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form id="saveForm" 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>
|
||||
@ -425,16 +427,20 @@
|
||||
<div />
|
||||
<div>
|
||||
{#each Object.keys(template) as oneService}
|
||||
<div class="flex flex-row border-b border-coolgray-500 my-6 space-x-2">
|
||||
<div
|
||||
class="flex flex-row my-6 space-x-2"
|
||||
class:border-b={template[oneService].environment.length > 0}
|
||||
class:border-coolgray-500={template[oneService].environment.length > 0}
|
||||
>
|
||||
<div class="title font-bold pb-3">{template[oneService].name}</div>
|
||||
<ServiceStatus id={template[oneService]} />
|
||||
<ServiceStatus id={oneService} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
{#if template[oneService].environment.length > 0}
|
||||
{#each template[oneService].environment as variable}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for={variable.name}>{variable.label}</label>
|
||||
<div class="grid grid-cols-2 items-center gap-2">
|
||||
<label class="h-10" for={variable.name}>{variable.label}</label>
|
||||
{#if variable.defaultValue === '$$generate_fqdn'}
|
||||
<input
|
||||
class="w-full"
|
||||
@ -444,9 +450,24 @@
|
||||
id={variable.name}
|
||||
value={service.fqdn}
|
||||
/>
|
||||
{:else if variable.defaultValue === 'true' || variable.defaultValue === 'false'}
|
||||
<select
|
||||
class="w-full font-normal"
|
||||
readonly={isDisabled}
|
||||
disabled={isDisabled}
|
||||
id={variable.name}
|
||||
name={variable.name}
|
||||
bind:value={variable.value}
|
||||
form="saveForm"
|
||||
>
|
||||
<option value="true">true</option>
|
||||
<option value="false"> false</option>
|
||||
</select>
|
||||
{:else}
|
||||
<input
|
||||
class="w-full"
|
||||
<CopyPasswordField
|
||||
isPasswordField={variable.defaultValue === '$$generate_password'}
|
||||
readonly={isDisabled}
|
||||
disabled={isDisabled}
|
||||
name={variable.name}
|
||||
id={variable.name}
|
||||
value={variable.value}
|
||||
|
@ -42,8 +42,16 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="name" class="pb-2 uppercase font-bold">name</label>
|
||||
{#each persistentStorages as storage}
|
||||
{#if persistentStorages.filter((s) => s.predefined).length > 0}
|
||||
<div class="text-base font-bold">Predefined Persistent Volumes</div>
|
||||
{/if}
|
||||
{#each persistentStorages.filter((s) => s.predefined) as storage}
|
||||
{#key storage.id}
|
||||
<Storage on:refresh={refreshStorage} {storage} />
|
||||
{/key}
|
||||
{/each}
|
||||
<div class="text-base font-bold" class:pt-10={persistentStorages.filter((s) => s.predefined).length > 0}>User Defined Persistent Volumes</div>
|
||||
{#each persistentStorages.filter((s) => !s.predefined) as storage}
|
||||
{#key storage.id}
|
||||
<Storage on:refresh={refreshStorage} {storage} />
|
||||
{/key}
|
||||
|
Loading…
x
Reference in New Issue
Block a user