From 3744c64459a79c3508beb0d7f3d882c5567ec0dc Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 22 Apr 2021 23:48:29 +0200 Subject: [PATCH] v1.0.9 (#37) Features: - Integrated the first service: [Plausible Analytics](https://plausible.io)! Fixes: - UI/UX fixes and new designs --- api/app.js | 2 + api/libs/applications/configuration.js | 23 +-- api/libs/common.js | 21 +- api/libs/services/plausible/index.js | 185 ++++++++++++++++++ api/routes/v1/dashboard/index.js | 15 +- api/routes/v1/databases/index.js | 28 ++- api/routes/v1/services/deploy.js | 15 ++ api/routes/v1/services/index.js | 27 +++ package.json | 4 +- pnpm-lock.yaml | 8 +- src/App.svelte | 2 +- .../Configuration/Configuration.svelte | 9 + .../Databases/SVGs/Clickhouse.svelte | 9 + src/components/PasswordField.svelte | 54 +++++ src/components/Services/Plausible.svelte | 82 ++++++++ src/index.css | 2 +- src/pages/_layout.svelte | 18 +- .../[name]/[branch]/overview.svelte | 2 +- src/pages/application/_layout.svelte | 2 +- src/pages/dashboard/applications.svelte | 6 +- src/pages/dashboard/databases.svelte | 47 +++-- src/pages/dashboard/services.svelte | 74 +++++++ .../{overview.svelte => configuration.svelte} | 46 ++--- src/pages/database/_layout.svelte | 52 +++-- src/pages/service/[name]/_layout.svelte | 81 ++++++++ src/pages/service/[name]/configuration.svelte | 68 +++++++ src/pages/service/[name]/index.svelte | 5 + src/pages/service/new/[type]/_layout.svelte | 33 ++++ src/pages/service/new/[type]/index.svelte | 136 +++++++++++++ src/pages/service/new/index.svelte | 32 +++ src/pages/settings/index.svelte | 3 +- src/store.js | 15 ++ 32 files changed, 997 insertions(+), 109 deletions(-) create mode 100644 api/libs/services/plausible/index.js create mode 100644 api/routes/v1/services/deploy.js create mode 100644 api/routes/v1/services/index.js create mode 100644 src/components/Databases/SVGs/Clickhouse.svelte create mode 100644 src/components/PasswordField.svelte create mode 100644 src/components/Services/Plausible.svelte create mode 100644 src/pages/dashboard/services.svelte rename src/pages/database/[name]/{overview.svelte => configuration.svelte} (73%) create mode 100644 src/pages/service/[name]/_layout.svelte create mode 100644 src/pages/service/[name]/configuration.svelte create mode 100644 src/pages/service/[name]/index.svelte create mode 100644 src/pages/service/new/[type]/_layout.svelte create mode 100644 src/pages/service/new/[type]/index.svelte create mode 100644 src/pages/service/new/index.svelte diff --git a/api/app.js b/api/app.js index d1b42fd35..f1fe5aed5 100644 --- a/api/app.js +++ b/api/app.js @@ -12,6 +12,8 @@ module.exports = async function (fastify, opts) { server.register(require('./routes/v1/application/deploy'), { prefix: '/application/deploy' }) server.register(require('./routes/v1/application/deploy/logs'), { prefix: '/application/deploy/logs' }) server.register(require('./routes/v1/databases'), { prefix: '/databases' }) + server.register(require('./routes/v1/services'), { prefix: '/services' }) + server.register(require('./routes/v1/services/deploy'), { prefix: '/services/deploy' }) server.register(require('./routes/v1/server'), { prefix: '/server' }) }) // Public routes diff --git a/api/libs/applications/configuration.js b/api/libs/applications/configuration.js index 53b913feb..134599a76 100644 --- a/api/libs/applications/configuration.js +++ b/api/libs/applications/configuration.js @@ -2,7 +2,7 @@ const { uniqueNamesGenerator, adjectives, colors, animals } = require('unique-na const cuid = require('cuid') const crypto = require('crypto') const { docker } = require('../docker') -const { execShellAsync } = require('../common') +const { execShellAsync, baseServiceConfiguration } = require('../common') function getUniq () { return uniqueNamesGenerator({ dictionaries: [adjectives, animals, colors], length: 2 }) @@ -15,25 +15,6 @@ function setDefaultConfiguration (configuration) { const shaBase = JSON.stringify({ repository: configuration.repository }) const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex') - const baseServiceConfiguration = { - replicas: 1, - restart_policy: { - condition: 'any', - max_attempts: 3 - }, - update_config: { - parallelism: 1, - delay: '10s', - order: 'start-first' - }, - rollback_config: { - parallelism: 1, - delay: '10s', - order: 'start-first', - failure_action: 'rollback' - } - } - configuration.build.container.name = sha256.slice(0, 15) configuration.general.nickname = nickname @@ -133,4 +114,4 @@ async function precheckDeployment ({ services, configuration }) { forceUpdate } } -module.exports = { setDefaultConfiguration, updateServiceLabels, precheckDeployment } +module.exports = { setDefaultConfiguration, updateServiceLabels, precheckDeployment, baseServiceConfiguration } diff --git a/api/libs/common.js b/api/libs/common.js index 6026af14b..0225e8224 100644 --- a/api/libs/common.js +++ b/api/libs/common.js @@ -6,6 +6,24 @@ const User = require('../models/User') const algorithm = 'aes-256-cbc' const key = process.env.SECRETS_ENCRYPTION_KEY +const baseServiceConfiguration = { + replicas: 1, + restart_policy: { + condition: 'any', + max_attempts: 3 + }, + update_config: { + parallelism: 1, + delay: '10s', + order: 'start-first' + }, + rollback_config: { + parallelism: 1, + delay: '10s', + order: 'start-first', + failure_action: 'rollback' + } +} function delay (t) { return new Promise(function (resolve) { setTimeout(function () { @@ -94,5 +112,6 @@ module.exports = { checkImageAvailable, encryptData, decryptData, - verifyUserId + verifyUserId, + baseServiceConfiguration } diff --git a/api/libs/services/plausible/index.js b/api/libs/services/plausible/index.js new file mode 100644 index 000000000..7e8c61fcc --- /dev/null +++ b/api/libs/services/plausible/index.js @@ -0,0 +1,185 @@ +const { execShellAsync, cleanupTmp, baseServiceConfiguration } = require('../../common') +const yaml = require('js-yaml') +const fs = require('fs').promises +const generator = require('generate-password') +const { docker } = require('../../docker') + +async function plausible ({ email, userName, userPassword, baseURL, traefikURL }) { + const deployId = 'plausible' + const workdir = '/tmp/plausible' + const secretKey = generator.generate({ length: 64, numbers: true, strict: true }) + const generateEnvsPostgres = { + POSTGRESQL_PASSWORD: generator.generate({ length: 24, numbers: true, strict: true }), + POSTGRESQL_USERNAME: generator.generate({ length: 10, numbers: true, strict: true }), + POSTGRESQL_DATABASE: 'plausible' + } + + const secrets = [ + { name: 'ADMIN_USER_EMAIL', value: email }, + { name: 'ADMIN_USER_NAME', value: userName }, + { name: 'ADMIN_USER_PWD', value: userPassword }, + { name: 'BASE_URL', value: baseURL }, + { name: 'SECRET_KEY_BASE', value: secretKey }, + { name: 'DISABLE_AUTH', value: 'false' }, + { name: 'DISABLE_REGISTRATION', value: 'true' }, + { name: 'DATABASE_URL', value: `postgresql://${generateEnvsPostgres.POSTGRESQL_USERNAME}:${generateEnvsPostgres.POSTGRESQL_PASSWORD}@plausible_db:5432/${generateEnvsPostgres.POSTGRESQL_DATABASE}` }, + { name: 'CLICKHOUSE_DATABASE_URL', value: 'http://plausible_events_db:8123/plausible' } + ] + + const generateEnvsClickhouse = {} + for (const secret of secrets) generateEnvsClickhouse[secret.name] = secret.value + + const clickhouseConfigXml = ` + + + warning + true + + + + + + + + + + ` + const clickhouseUserConfigXml = ` + + + + 0 + 0 + + + ` + + const clickhouseConfigs = [ + { source: 'plausible-clickhouse-user-config.xml', target: '/etc/clickhouse-server/users.d/logging.xml' }, + { source: 'plausible-clickhouse-config.xml', target: '/etc/clickhouse-server/config.d/logging.xml' }, + { source: 'plausible-init.query', target: '/docker-entrypoint-initdb.d/init.query' }, + { source: 'plausible-init-db.sh', target: '/docker-entrypoint-initdb.d/init-db.sh' } + ] + + const initQuery = 'CREATE DATABASE IF NOT EXISTS plausible;' + const initScript = 'clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query' + await execShellAsync(`mkdir -p ${workdir}`) + await fs.writeFile(`${workdir}/clickhouse-config.xml`, clickhouseConfigXml) + await fs.writeFile(`${workdir}/clickhouse-user-config.xml`, clickhouseUserConfigXml) + await fs.writeFile(`${workdir}/init.query`, initQuery) + await fs.writeFile(`${workdir}/init-db.sh`, initScript) + const stack = { + version: '3.8', + services: { + [deployId]: { + image: 'plausible/analytics:latest', + command: 'sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"', + networks: [`${docker.network}`], + volumes: [`${deployId}-postgres-data:/var/lib/postgresql/data`], + environment: generateEnvsClickhouse, + deploy: { + ...baseServiceConfiguration, + labels: [ + 'managedBy=coolify', + 'type=service', + 'serviceName=plausible', + 'configuration=' + JSON.stringify({ email, userName, userPassword, baseURL, secretKey, generateEnvsPostgres, generateEnvsClickhouse }), + 'traefik.enable=true', + 'traefik.http.services.' + + deployId + + '.loadbalancer.server.port=8000', + 'traefik.http.routers.' + + deployId + + '.entrypoints=websecure', + 'traefik.http.routers.' + + deployId + + '.rule=Host(`' + + traefikURL + + '`) && PathPrefix(`/`)', + 'traefik.http.routers.' + + deployId + + '.tls.certresolver=letsencrypt', + 'traefik.http.routers.' + + deployId + + '.middlewares=global-compress' + ] + } + }, + plausible_db: { + image: 'bitnami/postgresql:13.2.0', + networks: [`${docker.network}`], + environment: generateEnvsPostgres, + deploy: { + ...baseServiceConfiguration, + labels: [ + 'managedBy=coolify', + 'type=service', + 'serviceName=plausible' + ] + } + }, + plausible_events_db: { + image: 'yandex/clickhouse-server:21.3.2.5', + networks: [`${docker.network}`], + volumes: [`${deployId}-clickhouse-data:/var/lib/clickhouse`], + ulimits: { + nofile: { + soft: 262144, + hard: 262144 + } + }, + configs: [...clickhouseConfigs], + deploy: { + ...baseServiceConfiguration, + labels: [ + 'managedBy=coolify', + 'type=service', + 'serviceName=plausible' + ] + } + } + }, + networks: { + [`${docker.network}`]: { + external: true + } + }, + volumes: { + [`${deployId}-clickhouse-data`]: { + external: true + }, + [`${deployId}-postgres-data`]: { + external: true + } + }, + configs: { + 'plausible-clickhouse-user-config.xml': { + file: `${workdir}/clickhouse-user-config.xml` + }, + 'plausible-clickhouse-config.xml': { + file: `${workdir}/clickhouse-config.xml` + }, + 'plausible-init.query': { + file: `${workdir}/init.query` + }, + 'plausible-init-db.sh': { + file: `${workdir}/init-db.sh` + } + } + } + await fs.writeFile(`${workdir}/stack.yml`, yaml.dump(stack)) + await execShellAsync('docker stack rm plausible') + await execShellAsync( + `cat ${workdir}/stack.yml | docker stack deploy --prune -c - ${deployId}` + ) + cleanupTmp(workdir) +} + +async function activateAdminUser () { + const { POSTGRESQL_USERNAME, POSTGRESQL_PASSWORD, POSTGRESQL_DATABASE } = JSON.parse(JSON.parse((await execShellAsync('docker service inspect plausible_plausible --format=\'{{json .Spec.Labels.configuration}}\'')))).generateEnvsPostgres + const containers = (await execShellAsync('docker ps -a --format=\'{{json .Names}}\'')).replace(/"/g, '').trim().split('\n') + const postgresDB = containers.find(container => container.startsWith('plausible_plausible_db')) + await execShellAsync(`docker exec ${postgresDB} psql -H postgresql://${POSTGRESQL_USERNAME}:${POSTGRESQL_PASSWORD}@localhost:5432/${POSTGRESQL_DATABASE} -c "UPDATE users SET email_verified = true;"`) +} + +module.exports = { plausible, activateAdminUser } diff --git a/api/routes/v1/dashboard/index.js b/api/routes/v1/dashboard/index.js index 4205a66d8..acc5cf6ab 100644 --- a/api/routes/v1/dashboard/index.js +++ b/api/routes/v1/dashboard/index.js @@ -23,9 +23,10 @@ module.exports = async function (fastify) { } ]) const serverLogs = await ServerLog.find() - const services = await docker.engine.listServices() - let applications = services.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && r.Spec.Labels.configuration) - let databases = services.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'database' && r.Spec.Labels.configuration) + const dockerServices = await docker.engine.listServices() + let applications = dockerServices.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && r.Spec.Labels.configuration) + let databases = dockerServices.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'database' && r.Spec.Labels.configuration) + let services = dockerServices.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'service' && r.Spec.Labels.configuration) applications = applications.map(r => { if (JSON.parse(r.Spec.Labels.configuration)) { const configuration = JSON.parse(r.Spec.Labels.configuration) @@ -41,6 +42,11 @@ module.exports = async function (fastify) { r.Spec.Labels.configuration = configuration return r }) + services = services.map(r => { + const configuration = r.Spec.Labels.configuration ? JSON.parse(r.Spec.Labels.configuration) : null + r.Spec.Labels.configuration = configuration + return r + }) applications = [...new Map(applications.map(item => [item.Spec.Labels.configuration.publish.domain + item.Spec.Labels.configuration.publish.path, item])).values()] return { serverLogs, @@ -49,6 +55,9 @@ module.exports = async function (fastify) { }, databases: { deployed: databases + }, + services: { + deployed: services } } } catch (error) { diff --git a/api/routes/v1/databases/index.js b/api/routes/v1/databases/index.js index 9cdac684f..da0f34506 100644 --- a/api/routes/v1/databases/index.js +++ b/api/routes/v1/databases/index.js @@ -18,13 +18,15 @@ module.exports = async function (fastify) { const database = (await docker.engine.listServices()).find(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'database' && JSON.parse(r.Spec.Labels.configuration).general.deployId === deployId) if (database) { const jsonEnvs = {} - for (const d of database.Spec.TaskTemplate.ContainerSpec.Env) { - const s = d.split('=') - jsonEnvs[s[0]] = s[1] + if (database.Spec.TaskTemplate.ContainerSpec.Env) { + for (const d of database.Spec.TaskTemplate.ContainerSpec.Env) { + const s = d.split('=') + jsonEnvs[s[0]] = s[1] + } } const payload = { config: JSON.parse(database.Spec.Labels.configuration), - envs: jsonEnvs + envs: jsonEnvs || null } reply.code(200).send(payload) } else { @@ -39,7 +41,7 @@ module.exports = async function (fastify) { body: { type: 'object', properties: { - type: { type: 'string', enum: ['mongodb', 'postgresql', 'mysql', 'couchdb'] } + type: { type: 'string', enum: ['mongodb', 'postgresql', 'mysql', 'couchdb', 'clickhouse'] } }, required: ['type'] } @@ -82,9 +84,11 @@ module.exports = async function (fastify) { name: nickname } } + await execShellAsync(`mkdir -p ${configuration.general.workdir}`) let generateEnvs = {} let image = null let volume = null + let ulimits = {} if (type === 'mongodb') { generateEnvs = { MONGODB_ROOT_PASSWORD: passwords[0], @@ -119,6 +123,15 @@ module.exports = async function (fastify) { } image = 'bitnami/mysql:8.0' volume = `${configuration.general.deployId}-${type}-data:/bitnami/mysql/data` + } else if (type === 'clickhouse') { + image = 'yandex/clickhouse-server' + volume = `${configuration.general.deployId}-${type}-data:/var/lib/clickhouse` + ulimits = { + nofile: { + soft: 262144, + hard: 262144 + } + } } const stack = { @@ -129,6 +142,7 @@ module.exports = async function (fastify) { networks: [`${docker.network}`], environment: generateEnvs, volumes: [volume], + ulimits, deploy: { replicas: 1, update_config: { @@ -160,12 +174,12 @@ module.exports = async function (fastify) { } } } - await execShellAsync(`mkdir -p ${configuration.general.workdir}`) await fs.writeFile(`${configuration.general.workdir}/stack.yml`, yaml.dump(stack)) await execShellAsync( - `cat ${configuration.general.workdir}/stack.yml | docker stack deploy -c - ${configuration.general.deployId}` + `cat ${configuration.general.workdir}/stack.yml | docker stack deploy -c - ${configuration.general.deployId}` ) } catch (error) { + console.log(error) await saveServerLog(error) throw new Error(error) } diff --git a/api/routes/v1/services/deploy.js b/api/routes/v1/services/deploy.js new file mode 100644 index 000000000..481bf8b9b --- /dev/null +++ b/api/routes/v1/services/deploy.js @@ -0,0 +1,15 @@ +const { plausible, activateAdminUser } = require('../../../libs/services/plausible') + +module.exports = async function (fastify) { + fastify.post('/plausible', async (request, reply) => { + let { email, userName, userPassword, baseURL } = request.body + const traefikURL = baseURL + baseURL = `https://${baseURL}` + await plausible({ email, userName, userPassword, baseURL, traefikURL }) + return {} + }) + fastify.patch('/plausible/activate', async (request, reply) => { + await activateAdminUser() + return 'OK' + }) +} diff --git a/api/routes/v1/services/index.js b/api/routes/v1/services/index.js new file mode 100644 index 000000000..b9a5501f1 --- /dev/null +++ b/api/routes/v1/services/index.js @@ -0,0 +1,27 @@ +const { execShellAsync } = require('../../../libs/common') +const { docker } = require('../../../libs/docker') + +module.exports = async function (fastify) { + fastify.get('/:serviceName', async (request, reply) => { + const { serviceName } = request.params + try { + const service = (await docker.engine.listServices()).find(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'service' && r.Spec.Labels.serviceName === serviceName && r.Spec.Name === `${serviceName}_${serviceName}`) + if (service) { + const payload = { + config: JSON.parse(service.Spec.Labels.configuration) + } + reply.code(200).send(payload) + } else { + throw new Error() + } + } catch (error) { + console.log(error) + throw new Error('No service found?') + } + }) + fastify.delete('/:serviceName', async (request, reply) => { + const { serviceName } = request.params + await execShellAsync(`docker stack rm ${serviceName}`) + reply.code(200).send({}) + }) +} diff --git a/package.json b/package.json index c34c051ad..e8eeb15ef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.", - "version": "1.0.8", + "version": "1.0.9", "license": "AGPL-3.0", "scripts": { "lint": "standard", @@ -18,7 +18,7 @@ "dependencies": { "@iarna/toml": "^2.2.5", "@roxi/routify": "^2.15.1", - "@zerodevx/svelte-toast": "^0.2.1", + "@zerodevx/svelte-toast": "^0.2.2", "ajv": "^8.1.0", "axios": "^0.21.1", "commander": "^7.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1706c14f..7f8173149 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,7 +3,7 @@ lockfileVersion: 5.3 specifiers: '@iarna/toml': ^2.2.5 '@roxi/routify': ^2.15.1 - '@zerodevx/svelte-toast': ^0.2.1 + '@zerodevx/svelte-toast': ^0.2.2 ajv: ^8.1.0 axios: ^0.21.1 commander: ^7.2.0 @@ -45,7 +45,7 @@ specifiers: dependencies: '@iarna/toml': 2.2.5 '@roxi/routify': 2.15.1 - '@zerodevx/svelte-toast': 0.2.1 + '@zerodevx/svelte-toast': 0.2.2 ajv: 8.1.0 axios: 0.21.1 commander: 7.2.0 @@ -563,8 +563,8 @@ packages: resolution: {integrity: sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==} dev: true - /@zerodevx/svelte-toast/0.2.1: - resolution: {integrity: sha512-3yOusE+/xDaVNxkBJwbxDZea5ePQ77B15tbHv6ZlSYtlJu0u0PDhGMu8eoI+SmcCt4j+2sf0A1uS9+LcBIqUgg==} + /@zerodevx/svelte-toast/0.2.2: + resolution: {integrity: sha512-zriB7tSY54OEbRDqJ1NbHBv5Z83tWKhqqW7a+z8HMtZeR49zZUMLISFXmY7B8tMwzO6auB3A5dxuFyqB9+TZkQ==} dev: false /abab/2.0.5: diff --git a/src/App.svelte b/src/App.svelte index 8fe87ef35..77a76d6b3 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -31,7 +31,7 @@ @apply bg-warmGray-700 !important; } :global(input) { - @apply text-sm rounded py-2 px-6 font-bold bg-warmGray-800 text-white transition duration-150 outline-none !important; + @apply text-sm rounded py-2 px-6 font-bold bg-warmGray-800 text-white transition duration-150 outline-none border border-transparent !important; } :global(input:hover) { @apply bg-warmGray-700 !important; diff --git a/src/components/Databases/Configuration/Configuration.svelte b/src/components/Databases/Configuration/Configuration.svelte index cb453de45..0ba93ee52 100644 --- a/src/components/Databases/Configuration/Configuration.svelte +++ b/src/components/Databases/Configuration/Configuration.svelte @@ -58,6 +58,13 @@ > Couchdb + {#if type}
@@ -81,6 +88,8 @@ class:hover:bg-orange-500="{type === 'mysql'}" class:bg-red-600="{type === 'couchdb'}" class:hover:bg-red-500="{type === 'couchdb'}" + class:bg-yellow-500="{type === 'clickhouse'}" + class:hover:bg-yellow-400="{type === 'clickhouse'}" class="button p-2 w-32 text-white" on:click="{deploy}">Deploy diff --git a/src/components/Databases/SVGs/Clickhouse.svelte b/src/components/Databases/SVGs/Clickhouse.svelte new file mode 100644 index 000000000..b8cd28933 --- /dev/null +++ b/src/components/Databases/SVGs/Clickhouse.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/components/PasswordField.svelte b/src/components/PasswordField.svelte new file mode 100644 index 000000000..7d59299b9 --- /dev/null +++ b/src/components/PasswordField.svelte @@ -0,0 +1,54 @@ + + +
+ +
+ {#if showPassword} + + + + {:else} + + + + + {/if} +
+
diff --git a/src/components/Services/Plausible.svelte b/src/components/Services/Plausible.svelte new file mode 100644 index 000000000..fe74ab85a --- /dev/null +++ b/src/components/Services/Plausible.svelte @@ -0,0 +1,82 @@ + + +{#if loading} + +{:else} +
+
+
+
General
+
+ + + +
+ +
+
Domain
+ +
+
+
Email address
+ +
+
+
Username
+ +
+
+
Password
+ +
+
PostgreSQL
+
+
Username
+ +
+
+
Password
+ +
+
+
Database
+ +
+
+
+{/if} diff --git a/src/index.css b/src/index.css index 2b0db5aea..68f14a23a 100644 --- a/src/index.css +++ b/src/index.css @@ -34,7 +34,7 @@ body { font-family: 'Inter'; font-size: 16px; font-weight: 600; - white-space: normal; + white-space: normal; } [role~="tooltip"][data-microtip-position|="bottom"]::before { diff --git a/src/pages/_layout.svelte b/src/pages/_layout.svelte index f354764a4..ee6cd9f2d 100644 --- a/src/pages/_layout.svelte +++ b/src/pages/_layout.svelte @@ -145,7 +145,7 @@ coolLabs logo
+ +
+ + + +
+
+
+
+ {#if $deployments.services?.deployed.length > 0} +
+
+ {#each $deployments.services.deployed as service} +
+
+
+ {#if service.Spec.Labels.serviceName == "plausible"} +
+ plausible logo +
Plausible Analytics
+
+ {/if} +
+
+
+ {/each} +
+
+ {:else} +
No services found
+ {/if} +
diff --git a/src/pages/database/[name]/overview.svelte b/src/pages/database/[name]/configuration.svelte similarity index 73% rename from src/pages/database/[name]/overview.svelte rename to src/pages/database/[name]/configuration.svelte index 4899a4872..796ffa5d6 100644 --- a/src/pages/database/[name]/overview.svelte +++ b/src/pages/database/[name]/configuration.svelte @@ -1,19 +1,23 @@ {#await loadDatabaseConfig()} - + {:then}
-
-
+
+
+
Database
-
Connection string
+
Connection string
{#if $database.config.general.type === "mongodb"} - --> {/if}
@@ -83,8 +84,7 @@ import Loading from "../../../components/Loading.svelte"; + value="{$database.envs.MONGODB_ROOT_PASSWORD}">
{/if}
diff --git a/src/pages/database/_layout.svelte b/src/pages/database/_layout.svelte index 532f18ccb..2887d0f02 100644 --- a/src/pages/database/_layout.svelte +++ b/src/pages/database/_layout.svelte @@ -3,6 +3,7 @@ import { fetch, database, initialDatabase } from "@store"; import { toast } from "@zerodevx/svelte-toast"; import { onDestroy } from "svelte"; +import Tooltip from "../../components/Tooltip/Tooltip.svelte"; $: name = $params.name @@ -23,6 +24,7 @@ {/if}
diff --git a/src/pages/service/[name]/_layout.svelte b/src/pages/service/[name]/_layout.svelte new file mode 100644 index 000000000..f2f2704f8 --- /dev/null +++ b/src/pages/service/[name]/_layout.svelte @@ -0,0 +1,81 @@ + + + + + +
+ +
+ \ No newline at end of file diff --git a/src/pages/service/[name]/configuration.svelte b/src/pages/service/[name]/configuration.svelte new file mode 100644 index 000000000..b9f313a2c --- /dev/null +++ b/src/pages/service/[name]/configuration.svelte @@ -0,0 +1,68 @@ + + +{#await loadServiceConfig()} + +{:then} + +
+
+ {#if name === "plausible"} + + {/if} +
+
+{/await} diff --git a/src/pages/service/[name]/index.svelte b/src/pages/service/[name]/index.svelte new file mode 100644 index 000000000..a5319b72e --- /dev/null +++ b/src/pages/service/[name]/index.svelte @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/pages/service/new/[type]/_layout.svelte b/src/pages/service/new/[type]/_layout.svelte new file mode 100644 index 000000000..58c456b7d --- /dev/null +++ b/src/pages/service/new/[type]/_layout.svelte @@ -0,0 +1,33 @@ + + +{#await checkService()} + +{:then} +
+ +
+{/await} diff --git a/src/pages/service/new/[type]/index.svelte b/src/pages/service/new/[type]/index.svelte new file mode 100644 index 000000000..49f8899bb --- /dev/null +++ b/src/pages/service/new/[type]/index.svelte @@ -0,0 +1,136 @@ + + +
+
+ Deploy new + {#if type === "plausible"} + Plausible Analytics + {/if} +
+
+{#if loading} + +{:else} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+{/if} diff --git a/src/pages/service/new/index.svelte b/src/pages/service/new/index.svelte new file mode 100644 index 000000000..02d9f4aa1 --- /dev/null +++ b/src/pages/service/new/index.svelte @@ -0,0 +1,32 @@ + + +
+
+ Select a service +
+
+
+ {#if $isActive("/service/new")} +
+
+ plausible logo +
Plausible Analytics
+
+
+ {/if} +
diff --git a/src/pages/settings/index.svelte b/src/pages/settings/index.svelte index 00877a3c8..914c4e9da 100644 --- a/src/pages/settings/index.svelte +++ b/src/pages/settings/index.svelte @@ -37,7 +37,8 @@ {:then}
-
+
+
General
    diff --git a/src/store.js b/src/store.js index 40113d61a..ac722fbea 100644 --- a/src/store.js +++ b/src/store.js @@ -219,3 +219,18 @@ export const database = writable({ }) export const dbInprogress = writable(false) + +export const newService = writable({ + email: null, + userName: 'admin', + userPassword: null, + userPasswordAgain: null, + baseURL: null +}) +export const initialNewService = { + email: null, + userName: 'admin', + userPassword: null, + userPasswordAgain: null, + baseURL: null +}