fixes
This commit is contained in:
parent
aa27aeafa1
commit
dc626bd4f0
@ -1,5 +1,6 @@
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 1.0.3
|
||||
type: appwrite
|
||||
name: Appwrite
|
||||
documentation: https://appwrite.io/docs
|
||||
description: Secure Backend Server for Web, Mobile & Flutter Developers.
|
||||
@ -1035,6 +1036,7 @@
|
||||
The default value is 15 minutes.
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: latest
|
||||
type: weblate
|
||||
name: Weblate
|
||||
description: ''
|
||||
services:
|
||||
@ -1087,7 +1089,7 @@
|
||||
label: Weblate Admin Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- id: $$config_postgres_user
|
||||
main: $$id-postgresql
|
||||
name: POSTGRES_USER
|
||||
@ -1100,7 +1102,7 @@
|
||||
label: PostgreSQL Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- id: $$config_postgres_db
|
||||
main: $$id-postgresql
|
||||
name: POSTGRES_DB
|
||||
@ -1109,6 +1111,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 2022.10.14-1a5b0965
|
||||
type: searxng
|
||||
name: SearXNG
|
||||
description: ''
|
||||
services:
|
||||
@ -1181,6 +1184,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v2.0.6
|
||||
type: glitchtip
|
||||
name: GlitchTip
|
||||
description: ''
|
||||
services:
|
||||
@ -1344,6 +1348,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v2.13.0
|
||||
type: hasura
|
||||
name: Hasura
|
||||
description: 'Instant realtime GraphQL APIs on any Postgres application, existing or new.'
|
||||
services:
|
||||
@ -1388,7 +1393,7 @@
|
||||
label: Hasura Admin Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- id: $$config_postgres_user
|
||||
name: POSTGRES_USER
|
||||
label: PostgreSQL User
|
||||
@ -1406,6 +1411,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: postgresql-v1.38.0
|
||||
type: umami
|
||||
name: Umami
|
||||
description: >-
|
||||
Umami is a simple, easy to use, self-hosted web analytics solution. The goal
|
||||
@ -1606,9 +1612,10 @@
|
||||
label: Initial Admin Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v0.29.1
|
||||
type: meilisearch
|
||||
name: MeiliSearch
|
||||
description: >-
|
||||
MeiliSearch is a lightning Fast, Ultra Relevant, and Typo-Tolerant Search
|
||||
@ -1634,9 +1641,10 @@
|
||||
label: Master Key
|
||||
defaultValue: $$generate_hex(64)
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: latest
|
||||
type: ghost
|
||||
name: Ghost
|
||||
description: >-
|
||||
Ghost is a free and open source blogging platform written in JavaScript and
|
||||
@ -1749,8 +1757,9 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: php8.1
|
||||
name: WordPress
|
||||
description: WordPress is a content management system based on PHP.
|
||||
type: wordpress
|
||||
name: WordPress (with MySQL)
|
||||
description: WordPress is a content management system based on PHP with new MySQL database.
|
||||
services:
|
||||
$$id:
|
||||
name: WordPress
|
||||
@ -1789,53 +1798,138 @@
|
||||
label: WordPress DB Host
|
||||
defaultValue: $$id-mysql
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_wordpress_db_user
|
||||
name: WORDPRESS_DB_USER
|
||||
label: WordPress DB User
|
||||
defaultValue: $$config_mysql_user
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$secret_wordpress_db_password
|
||||
name: WORDPRESS_DB_PASSWORD
|
||||
label: WordPress DB Password
|
||||
defaultValue: $$secret_mysql_password
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_wordpress_db_name
|
||||
name: WORDPRESS_DB_NAME
|
||||
label: WordPress DB Name
|
||||
defaultValue: $$config_mysql_database
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_wordpress_config_extra
|
||||
name: WORDPRESS_CONFIG_EXTRA
|
||||
label: WordPress Config Extra
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
type: 'textarea'
|
||||
placeholder: |
|
||||
define('WP_DEBUG', true);
|
||||
define('WP_DEBUG_LOG', true);
|
||||
define('WP_DEBUG_DISPLAY', false);
|
||||
@ini_set('display_errors', 0);
|
||||
- id: $$secret_mysql_root_password
|
||||
name: MYSQL_ROOT_PASSWORD
|
||||
label: MySQL Root Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_mysql_root_user
|
||||
name: MYSQL_ROOT_USER
|
||||
label: MySQL Root User
|
||||
defaultValue: $$generate_username
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_mysql_database
|
||||
name: MYSQL_DATABASE
|
||||
label: MySQL Database
|
||||
defaultValue: wordpress
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$config_mysql_user
|
||||
name: MYSQL_USER
|
||||
label: MySQL User
|
||||
defaultValue: $$generate_username
|
||||
description: ''
|
||||
readonly: true
|
||||
- id: $$secret_mysql_password
|
||||
name: MYSQL_PASSWORD
|
||||
label: MySQL Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
readonly: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: php8.1
|
||||
type: wordpress-only
|
||||
name: WordPress (without MySQL)
|
||||
description: WordPress is a content management system based on PHP without MySQL.
|
||||
services:
|
||||
$$id:
|
||||
name: WordPress
|
||||
documentation: 'Taken from https://docs.docker.com/compose/wordpress/'
|
||||
image: 'wordpress:$$core_version'
|
||||
volumes:
|
||||
- '$$id-wordpress-data:/var/www/html'
|
||||
environment:
|
||||
- WORDPRESS_DB_HOST=$$config_wordpress_db_host
|
||||
- WORDPRESS_DB_PORT=$$config_wordpress_db_port
|
||||
- WORDPRESS_DB_USER=$$config_wordpress_db_user
|
||||
- WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password
|
||||
- WORDPRESS_DB_NAME=$$config_wordpress_db_name
|
||||
- WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra
|
||||
ports:
|
||||
- '80'
|
||||
variables:
|
||||
- id: $$config_wordpress_db_host
|
||||
name: WORDPRESS_DB_HOST
|
||||
label: Database Host
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
placeholder: 'db.coollabs.io'
|
||||
required: true
|
||||
- id: $$config_wordpress_db_port
|
||||
name: WORDPRESS_DB_PORT
|
||||
label: Database Port
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
placeholder: '3306'
|
||||
required: true
|
||||
- id: $$config_wordpress_db_user
|
||||
name: WORDPRESS_DB_USER
|
||||
label: Database User
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
placeholder: 'wordpress'
|
||||
required: true
|
||||
- id: $$secret_wordpress_db_password
|
||||
name: WORDPRESS_DB_PASSWORD
|
||||
label: Database Password
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
placeholder: 'supers3cr3tpassw0rd!'
|
||||
required: true
|
||||
showOnConfiguration: true
|
||||
- id: $$config_wordpress_db_name
|
||||
name: WORDPRESS_DB_NAME
|
||||
label: Database Name
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
placeholder: 'wordpress'
|
||||
required: true
|
||||
- id: $$config_wordpress_config_extra
|
||||
name: WORDPRESS_CONFIG_EXTRA
|
||||
label: Extra Config
|
||||
defaultValue: ''
|
||||
description: ''
|
||||
type: 'textarea'
|
||||
placeholder: |
|
||||
define('WP_DEBUG', true);
|
||||
define('WP_DEBUG_LOG', true);
|
||||
define('WP_DEBUG_DISPLAY', false);
|
||||
@ini_set('display_errors', 0);
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 4.7.1
|
||||
type: vscodeserver
|
||||
name: VSCode Server
|
||||
description: >-
|
||||
vscode-server by Coder is VS Code running on a remote server, accessible
|
||||
@ -1861,9 +1955,10 @@
|
||||
label: Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: RELEASE.2022-10-15T19-57-03Z
|
||||
type: minio
|
||||
name: MinIO
|
||||
description: ' MinIO is a cloud storage server compatible with Amazon S3'
|
||||
services:
|
||||
@ -1915,9 +2010,10 @@
|
||||
label: Root User Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.21.1
|
||||
type: fider
|
||||
name: Fider
|
||||
description: Fider is a platform to collect and organize customer feedback.
|
||||
services:
|
||||
@ -2032,6 +2128,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.198.1
|
||||
type: n8nio
|
||||
name: n8n.io
|
||||
description: n8n is a free and open node based Workflow Automation Tool.
|
||||
services:
|
||||
@ -2056,6 +2153,7 @@
|
||||
description: ''
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: stable
|
||||
type: plausibleanalytics
|
||||
name: Plausible Analytics
|
||||
description: Plausible is a lightweight and open-source website analytics tool.
|
||||
services:
|
||||
@ -2157,7 +2255,7 @@
|
||||
label: Admin User Password
|
||||
defaultValue: $$generate_password
|
||||
description: This is the admin password. Please change it.
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- id: $$secret_secret_key_base
|
||||
name: SECRET_KEY_BASE
|
||||
label: Secret Key Base
|
||||
@ -2185,7 +2283,7 @@
|
||||
label: PostgreSQL Password
|
||||
defaultValue: $$generate_password
|
||||
description: ''
|
||||
showOnUI: true
|
||||
showOnConfiguration: true
|
||||
- id: $$config_postgresql_database
|
||||
main: $$id-postgresql
|
||||
name: POSTGRESQL_DATABASE
|
||||
@ -2199,6 +2297,7 @@
|
||||
description: This is the default script name.
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.98.1
|
||||
type: nocodb
|
||||
name: NocoDB
|
||||
description: >-
|
||||
The Open Source Airtable Alternative - Turns any MySQL, PostgreSQL, SQL
|
||||
|
@ -0,0 +1,32 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Wordpress" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"extraConfig" TEXT,
|
||||
"tablePrefix" TEXT,
|
||||
"ownMysql" BOOLEAN NOT NULL DEFAULT false,
|
||||
"mysqlHost" TEXT,
|
||||
"mysqlPort" INTEGER,
|
||||
"mysqlUser" TEXT,
|
||||
"mysqlPassword" TEXT,
|
||||
"mysqlRootUser" TEXT,
|
||||
"mysqlRootUserPassword" TEXT,
|
||||
"mysqlDatabase" TEXT,
|
||||
"mysqlPublicPort" INTEGER,
|
||||
"ftpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"ftpUser" TEXT,
|
||||
"ftpPassword" TEXT,
|
||||
"ftpPublicPort" INTEGER,
|
||||
"ftpHostKey" TEXT,
|
||||
"ftpHostKeyPrivate" TEXT,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress";
|
||||
DROP TABLE "Wordpress";
|
||||
ALTER TABLE "new_Wordpress" RENAME TO "Wordpress";
|
||||
CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
@ -480,10 +480,10 @@ model Wordpress {
|
||||
ownMysql Boolean @default(false)
|
||||
mysqlHost String?
|
||||
mysqlPort Int?
|
||||
mysqlUser String
|
||||
mysqlPassword String
|
||||
mysqlRootUser String
|
||||
mysqlRootUserPassword String
|
||||
mysqlUser String?
|
||||
mysqlPassword String?
|
||||
mysqlRootUser String?
|
||||
mysqlRootUserPassword String?
|
||||
mysqlDatabase String?
|
||||
mysqlPublicPort Int?
|
||||
ftpEnabled Boolean @default(false)
|
||||
|
@ -8,6 +8,7 @@ const template = yaml.load(templateYml)
|
||||
const newTemplate = {
|
||||
"templateVersion": "1.0.0",
|
||||
"defaultVersion": "latest",
|
||||
"type": "",
|
||||
"name": "",
|
||||
"description": "",
|
||||
"services": {
|
||||
@ -18,6 +19,7 @@ const newTemplate = {
|
||||
const version = template.caproverOneClickApp.variables.find(v => v.id === '$$cap_APP_VERSION' || v.id === '$$cap_version').defaultValue || 'latest'
|
||||
|
||||
newTemplate.name = template.caproverOneClickApp.displayName
|
||||
newTemplate.type = template.caproverOneClickApp.displayName.replaceAll(' ', '').toLowerCase()
|
||||
newTemplate.documentation = template.caproverOneClickApp.documentation
|
||||
newTemplate.description = template.caproverOneClickApp.description
|
||||
newTemplate.defaultVersion = version
|
||||
|
@ -7,19 +7,32 @@ const templates = await fs.readFile('../devTemplates.yaml', 'utf8');
|
||||
const devTemplates = yaml.load(templates);
|
||||
for (const template of devTemplates) {
|
||||
let image = template.services['$$id'].image.replaceAll(':$$core_version', '');
|
||||
const name = template.name
|
||||
if (!image.includes('/')) {
|
||||
image = `library/${image}`;
|
||||
}
|
||||
repositories.push({ image, name: name.toLowerCase().replaceAll(' ', '') });
|
||||
repositories.push({ image, name: template.type });
|
||||
}
|
||||
const services = []
|
||||
|
||||
const numberOfTags = 30;
|
||||
// const semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/g)
|
||||
const semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/g)
|
||||
for (const repository of repositories) {
|
||||
console.log('Querying', repository.name, 'at', repository.image);
|
||||
let semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/g)
|
||||
if (repository.name.startsWith('wordpress')) {
|
||||
semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-php(0|[1-9]\d*)$/g)
|
||||
}
|
||||
if (repository.name.startsWith('minio')) {
|
||||
semverRegex = new RegExp(/^RELEASE.*$/g)
|
||||
}
|
||||
if (repository.name.startsWith('fider')) {
|
||||
semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g)
|
||||
}
|
||||
if (repository.name.startsWith('searxng')) {
|
||||
semverRegex = new RegExp(/^\d{4}[\.\-](0?[1-9]|[12][0-9]|3[01])[\.\-](0?[1-9]|1[012]).*$/)
|
||||
}
|
||||
if (repository.name.startsWith('umami')) {
|
||||
semverRegex = new RegExp(/^postgresql-v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g)
|
||||
}
|
||||
if (repository.image.includes('ghcr.io')) {
|
||||
const { execaCommand } = await import('execa');
|
||||
const { stdout } = await execaCommand(`docker run --rm quay.io/skopeo/stable list-tags docker://${repository.image}`);
|
||||
|
@ -317,7 +317,9 @@ async function wordpress(service: any, template: any) {
|
||||
]
|
||||
await migrateSettings(settings, service, template);
|
||||
await migrateSecrets(secrets, service);
|
||||
|
||||
if (ownMysql) {
|
||||
await prisma.service.update({ where: { id: service.id }, data: { type: "wordpress-only" } })
|
||||
}
|
||||
// Remove old service data
|
||||
// await prisma.service.update({ where: { id: service.id }, data: { wordpress: { delete: true } } })
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ export function getAPIUrl() {
|
||||
if (process.env.CODESANDBOX_HOST) {
|
||||
return `https://${process.env.CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`;
|
||||
}
|
||||
return isDev ? 'http://localhost:3001' : 'http://localhost:3000';
|
||||
return isDev ? 'http://host.docker.internal:3001' : 'http://localhost:3000';
|
||||
}
|
||||
|
||||
export function getUIUrl() {
|
||||
@ -1447,12 +1447,13 @@ export async function getServiceFromDB({
|
||||
persistentStorage: true,
|
||||
serviceSecret: true,
|
||||
serviceSetting: true,
|
||||
wordpress: true
|
||||
}
|
||||
});
|
||||
if (!body) {
|
||||
return null
|
||||
}
|
||||
body.type = fixType(body.type);
|
||||
// body.type = fixType(body.type);
|
||||
|
||||
if (body?.serviceSecret.length > 0) {
|
||||
body.serviceSecret = body.serviceSecret.map((s) => {
|
||||
@ -1460,6 +1461,9 @@ export async function getServiceFromDB({
|
||||
return s;
|
||||
});
|
||||
}
|
||||
if (body.wordpress) {
|
||||
body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
|
||||
}
|
||||
|
||||
return { ...body, settings };
|
||||
}
|
||||
|
@ -42,11 +42,16 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
const template: any = await parseAndFindServiceTemplates(service, workdir, true)
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const config = {};
|
||||
for (const service in template.services) {
|
||||
const isWordpress = type === 'wordpress';
|
||||
for (const s in template.services) {
|
||||
if (isWordpress && service.wordpress.ownMysql && s.name === 'MySQL') {
|
||||
console.log('skipping predefined mysql')
|
||||
continue;
|
||||
}
|
||||
let newEnvironments = []
|
||||
if (arm) {
|
||||
if (template.services[service]?.environmentArm?.length > 0) {
|
||||
for (const environment of template.services[service].environmentArm) {
|
||||
if (template.services[s]?.environmentArm?.length > 0) {
|
||||
for (const environment of template.services[s].environmentArm) {
|
||||
const [env, value] = environment.split("=");
|
||||
if (!value.startsWith('$$secret') && value !== '') {
|
||||
newEnvironments.push(`${env}=${value}`)
|
||||
@ -54,8 +59,8 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (template.services[service]?.environment?.length > 0) {
|
||||
for (const environment of template.services[service].environment) {
|
||||
if (template.services[s]?.environment?.length > 0) {
|
||||
for (const environment of template.services[s].environment) {
|
||||
const [env, value] = environment.split("=");
|
||||
if (!value.startsWith('$$secret') && value !== '') {
|
||||
newEnvironments.push(`${env}=${value}`)
|
||||
@ -68,7 +73,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
for (const secret of secrets) {
|
||||
const { name, value } = secret
|
||||
if (value) {
|
||||
const foundEnv = !!template.services[service].environment?.find(env => env.startsWith(`${name}=`))
|
||||
const foundEnv = !!template.services[s].environment?.find(env => env.startsWith(`${name}=`))
|
||||
const foundNewEnv = !!newEnvironments?.find(env => env.startsWith(`${name}=`))
|
||||
if (foundEnv && !foundNewEnv) {
|
||||
newEnvironments.push(`${name}=${decrypt(value)}`)
|
||||
@ -76,7 +81,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
}
|
||||
}
|
||||
const customVolumes = await prisma.servicePersistentStorage.findMany({ where: { serviceId: id } })
|
||||
let volumes = arm ? template.services[service].volumesArm : template.services[service].volumes
|
||||
let volumes = arm ? template.services[s].volumesArm : template.services[s].volumes
|
||||
if (customVolumes.length > 0) {
|
||||
for (const customVolume of customVolumes) {
|
||||
const { volumeName, path, containerId } = customVolume
|
||||
@ -85,41 +90,58 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
config[service] = {
|
||||
container_name: service,
|
||||
build: template.services[service].build || undefined,
|
||||
command: template.services[service].command,
|
||||
entrypoint: template.services[service]?.entrypoint,
|
||||
image: arm ? template.services[service].imageArm : template.services[service].image,
|
||||
expose: template.services[service].ports,
|
||||
if (isWordpress) {
|
||||
const { wordpress: { mysqlHost, mysqlPort, mysqlUser, mysqlPassword, mysqlDatabase, ownMysql } } = service
|
||||
console.log({ mysqlHost, mysqlPort, mysqlUser, mysqlPassword, mysqlDatabase, ownMysql })
|
||||
if (ownMysql) {
|
||||
let envsToRemove = ['WORDPRESS_DB_HOST', 'WORDPRESS_DB_USER', 'WORDPRESS_DB_PASSWORD', 'WORDPRESS_DB_NAME']
|
||||
for (const env of newEnvironments) {
|
||||
if (envsToRemove.includes(env.split('=')[0])) {
|
||||
console.log('removing', env)
|
||||
newEnvironments = newEnvironments.filter(e => e !== env)
|
||||
}
|
||||
}
|
||||
newEnvironments.push(`WORDPRESS_DB_HOST=${mysqlHost}:${mysqlPort}`)
|
||||
newEnvironments.push(`WORDPRESS_DB_USER=${mysqlUser}`)
|
||||
newEnvironments.push(`WORDPRESS_DB_PASSWORD=${mysqlPassword}`)
|
||||
newEnvironments.push(`WORDPRESS_DB_NAME=${mysqlDatabase}`)
|
||||
}
|
||||
}
|
||||
config[s] = {
|
||||
container_name: s,
|
||||
build: template.services[s].build || undefined,
|
||||
command: template.services[s].command,
|
||||
entrypoint: template.services[s]?.entrypoint,
|
||||
image: arm ? template.services[s].imageArm : template.services[s].image,
|
||||
expose: template.services[s].ports,
|
||||
...(exposePort ? { ports: [`${exposePort}:${exposePort}`] } : {}),
|
||||
volumes,
|
||||
environment: newEnvironments,
|
||||
depends_on: template.services[service]?.depends_on,
|
||||
ulimits: template.services[service]?.ulimits,
|
||||
cap_drop: template.services[service]?.cap_drop,
|
||||
cap_add: template.services[service]?.cap_add,
|
||||
depends_on: template.services[s]?.depends_on,
|
||||
ulimits: template.services[s]?.ulimits,
|
||||
cap_drop: template.services[s]?.cap_drop,
|
||||
cap_add: template.services[s]?.cap_add,
|
||||
labels: makeLabelForServices(type),
|
||||
...defaultComposeConfiguration(network),
|
||||
}
|
||||
|
||||
// Generate files for builds
|
||||
if (template.services[service]?.files?.length > 0) {
|
||||
if (!template.services[service].build) {
|
||||
template.services[service].build = {
|
||||
if (template.services[s]?.files?.length > 0) {
|
||||
if (!template.services[s].build) {
|
||||
template.services[s].build = {
|
||||
context: workdir,
|
||||
dockerfile: `Dockerfile.${service}`
|
||||
dockerfile: `Dockerfile.${s}`
|
||||
}
|
||||
}
|
||||
let Dockerfile = `
|
||||
FROM ${template.services[service].image}`
|
||||
for (const file of template.services[service].files) {
|
||||
FROM ${template.services[s].image}`
|
||||
for (const file of template.services[s].files) {
|
||||
const { source, destination, content } = file;
|
||||
await fs.writeFile(source, content);
|
||||
Dockerfile += `
|
||||
COPY ./${path.basename(source)} ${destination}`
|
||||
}
|
||||
await fs.writeFile(`${workdir}/Dockerfile.${service}`, Dockerfile);
|
||||
await fs.writeFile(`${workdir}/Dockerfile.${servsice}`, Dockerfile);
|
||||
}
|
||||
}
|
||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||
|
@ -113,7 +113,7 @@ export async function getServiceStatus(request: FastifyRequest<OnlyId>) {
|
||||
}
|
||||
export async function parseAndFindServiceTemplates(service: any, workdir?: string, isDeploy: boolean = false) {
|
||||
const templates = await getTemplates()
|
||||
const foundTemplate = templates.find(t => fixType(t.name) === service.type)
|
||||
const foundTemplate = templates.find(t => fixType(t.type) === service.type)
|
||||
let parsedTemplate = {}
|
||||
if (foundTemplate) {
|
||||
if (!isDeploy) {
|
||||
@ -130,16 +130,21 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
|
||||
for (const env of value.environment) {
|
||||
const [envKey, envValue] = env.split('=')
|
||||
const variable = foundTemplate.variables.find(v => v.name === envKey) || foundTemplate.variables.find(v => v.id === envValue)
|
||||
const id = variable.id.replaceAll('$$', '')
|
||||
const label = variable?.label
|
||||
const description = variable?.description
|
||||
const defaultValue = variable?.defaultValue
|
||||
const main = variable?.main || '$$id'
|
||||
if (envValue.startsWith('$$config') || variable?.showOnUI) {
|
||||
const type = variable?.type || 'input'
|
||||
const placeholder = variable?.placeholder || ''
|
||||
const readonly = variable?.readonly || false
|
||||
const required = variable?.required || false
|
||||
if (envValue.startsWith('$$config') || variable?.showOnConfiguration) {
|
||||
if (envValue.startsWith('$$config_coolify')) {
|
||||
continue
|
||||
}
|
||||
parsedTemplate[realKey].environment.push(
|
||||
{ name: envKey, value: envValue, main, label, description, defaultValue }
|
||||
{ id, name: envKey, value: envValue, main, label, description, defaultValue, type, placeholder, required, readonly }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -203,6 +208,9 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
|
||||
if (value) {
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, bcrypt.hashSync(value, 10) + "\"")
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, value + "\"")
|
||||
} else {
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, "\"")
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, "\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,7 +257,7 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
|
||||
const { id } = request.params;
|
||||
const { type } = request.body;
|
||||
const templates = await getTemplates()
|
||||
let foundTemplate = templates.find(t => fixType(t.name) === fixType(type))
|
||||
let foundTemplate = templates.find(t => fixType(t.type) === fixType(type))
|
||||
if (foundTemplate) {
|
||||
foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id))
|
||||
if (foundTemplate.variables.length > 0) {
|
||||
@ -307,6 +315,10 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
|
||||
}
|
||||
}
|
||||
await prisma.service.update({ where: { id }, data: { type, version: foundTemplate.defaultVersion, templateVersion: foundTemplate.templateVersion } })
|
||||
|
||||
if (type.startsWith('wordpress')) {
|
||||
await prisma.service.update({ where: { id }, data: { wordpress: { create: {} } } })
|
||||
}
|
||||
return reply.code(201).send()
|
||||
} else {
|
||||
throw { status: 404, message: 'Service type not found.' }
|
||||
@ -480,7 +492,7 @@ export async function saveService(request: FastifyRequest<SaveService>, reply: F
|
||||
}
|
||||
const templates = await getTemplates()
|
||||
const service = await prisma.service.findUnique({ where: { id } })
|
||||
const foundTemplate = templates.find(t => fixType(t.name) === fixType(service.type))
|
||||
const foundTemplate = templates.find(t => fixType(t.type) === fixType(service.type))
|
||||
for (const setting of serviceSetting) {
|
||||
let { id: settingId, name, value, changed = false, isNew = false, variableName } = setting
|
||||
if (changed) {
|
||||
@ -506,11 +518,19 @@ export async function saveService(request: FastifyRequest<SaveService>, reply: F
|
||||
export async function getServiceSecrets(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user.teamId;
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
let secrets = await prisma.serviceSecret.findMany({
|
||||
where: { serviceId: id },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
const templates = await getTemplates()
|
||||
const foundTemplate = templates.find(t => fixType(t.type) === service.type)
|
||||
secrets = secrets.map((secret) => {
|
||||
const foundVariable = foundTemplate?.variables.find(v => v.name === secret.name) || null
|
||||
if (foundVariable) {
|
||||
secret.readonly = foundVariable.readonly
|
||||
}
|
||||
secret.value = decrypt(secret.value);
|
||||
return secret;
|
||||
});
|
||||
|
@ -381,7 +381,7 @@ export async function traefikConfiguration(request, reply) {
|
||||
} = service;
|
||||
if (destinationDockerId) {
|
||||
const templates = await getTemplates();
|
||||
let found = templates.find((a) => fixType(a.name) === fixType(type));
|
||||
let found = templates.find((a) => a.type === type);
|
||||
if (found) {
|
||||
found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id));
|
||||
for (const oneService of Object.keys(found.services)) {
|
||||
@ -509,7 +509,7 @@ export async function traefikConfiguration(request, reply) {
|
||||
}
|
||||
} else {
|
||||
traefik.http.routers[`${id}-${port || 'default'}`] = generateHttpRouter(`${id}-${port || 'default'}`, nakedDomain, pathPrefix)
|
||||
traefik.http.routers[`${id}-${port || 'default'}-secure`] = generateProtocolRedirectRouter(`${id}-${port || 'default'}-secure`, nakedDomain, pathPrefix, 'https-to-http')
|
||||
traefik.http.routers[`${id}-${port || 'default'}-secure`] = generateProtocolRedirectRouter(`${id}-${port || 'default'}`, nakedDomain, pathPrefix, 'https-to-http')
|
||||
traefik.http.services[`${id}-${port || 'default'}`] = generateLoadBalancerService(id, port)
|
||||
|
||||
if (!dualCerts) {
|
||||
@ -873,7 +873,7 @@ export async function remoteTraefikConfiguration(request: FastifyRequest<OnlyId>
|
||||
} = service;
|
||||
if (destinationDockerId) {
|
||||
const templates = await getTemplates();
|
||||
let found = templates.find((a) => fixType(a.name) === fixType(type));
|
||||
let found = templates.find((a) => a.type === type);
|
||||
if (found) {
|
||||
const port = found.ports.main;
|
||||
const publicPort = service[type]?.publicPort;
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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} />
|
||||
|
@ -41,7 +41,7 @@
|
||||
"saving": "Saving...",
|
||||
"name": "Name",
|
||||
"value": "Value",
|
||||
"action": "Action",
|
||||
"action": "Actions",
|
||||
"is_required": "is required.",
|
||||
"add": "Add",
|
||||
"set": "Set",
|
||||
|
@ -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>
|
||||
|
86
apps/ui/src/routes/services/[id]/_Services/wordpress.svelte
Normal file
86
apps/ui/src/routes/services/[id]/_Services/wordpress.svelte
Normal 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}
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}`);
|
||||
|
Loading…
x
Reference in New Issue
Block a user