diff --git a/apps/api/src/lib.ts b/apps/api/src/lib.ts index 07a7445b0..5e8461489 100644 --- a/apps/api/src/lib.ts +++ b/apps/api/src/lib.ts @@ -16,6 +16,7 @@ export async function migrateServicesToNewTemplate() { if (service.type === 'meilisearch' && service.meiliSearch) await meilisearch(service) if (service.type === 'umami' && service.umami) await umami(service) if (service.type === 'hasura' && service.hasura) await hasura(service) + if (service.type === 'glitchtip' && service.glitchTip) await glitchtip(service) await createVolumes(service); } } catch (error) { @@ -23,6 +24,39 @@ export async function migrateServicesToNewTemplate() { } } +async function glitchtip(service: any) { + const { postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, defaultEmail, defaultUsername, defaultPassword, defaultEmailFrom, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpUseTls, emailSmtpUseSsl, emailBackend, mailgunApiKey, sendgridApiKey, enableOpenUserRegistration } = service.glitchTip + + const secrets = [ + `POSTGRES_PASSWORD@@@${postgresqlPassword}`, + `SECRET_KEY@@@${secretKeyBase}`, + `DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@$$generate_fqdn:5432/${postgresqlDatabase}`)}`, + `REDIS_URL@@@${encrypt(`redis://$$generate_fqdn:6379`)}`, + `EMAIL_HOST_PASSWORD@@@${emailSmtpPassword}`, + `MAILGUN_API_KEY@@@${mailgunApiKey}`, + `SENDGRID_API_KEY@@@${sendgridApiKey}`, + `DJANGO_SUPERUSER_PASSWORD@@@${defaultPassword}`, + ] + const settings = [ + `POSTGRES_USER@@@${postgresqlUser}`, + `POSTGRES_DB@@@${postgresqlDatabase}`, + `DEFAULT_FROM_EMAIL@@@${defaultEmailFrom}`, + `EMAIL_HOST@@@${emailSmtpHost}`, + `EMAIL_PORT@@@${emailSmtpPort}`, + `EMAIL_HOST_USER@@@${emailSmtpUser}`, + `EMAIL_USE_TLS@@@${emailSmtpUseTls}`, + `EMAIL_USE_SSL@@@${emailSmtpUseSsl}`, + `EMAIL_BACKEND@@@${emailBackend}`, + `ENABLE_OPEN_USER_REGISTRATION@@@${enableOpenUserRegistration}`, + `DJANGO_SUPERUSER_EMAIL@@@${defaultEmail}`, + `DJANGO_SUPERUSER_USERNAME@@@${defaultUsername}`, + ] + await migrateSecrets(secrets, service); + await migrateSettings(settings, service); + + // Remove old service data + // await prisma.service.update({ where: { id: service.id }, data: { wordpress: { delete: true } } }) +} async function hasura(service: any) { const { postgresqlUser, postgresqlPassword, postgresqlDatabase, graphQLAdminPassword } = service.hasura diff --git a/apps/api/src/lib/templates.ts b/apps/api/src/lib/templates.ts index e2c995cd7..0ed33d45f 100644 --- a/apps/api/src/lib/templates.ts +++ b/apps/api/src/lib/templates.ts @@ -1,4 +1,218 @@ export default [ + { + "templateVersion": "1.0.0", + "serviceDefaultVersion": "v2.0.6", + "name": "glitchtip", + "displayName": "GlitchTip", + "description": "", + "services": { + "$$id": { + "name": "GlitchTip", + "depends_on": [ + "$$id-postgresql" + ], + "image": "glitchtip/glitchtip:$$core_version", + "volumes": [], + "environment": [ + "PORT=3000", + "GLITCHTIP_DOMAIN=$$config_glitchtip_domain", + "SECRET_KEY=$$secret_secret_key", + "DATABASE_URL=$$secret_database_url", + "REDIS_URL=$$secret_redis_url", + "DEFAULT_FROM_EMAIL=$$config_default_from_email", + "EMAIL_HOST=$$config_email_host", + "EMAIL_PORT=$$config_email_port", + "EMAIL_HOST_USER=$$config_email_host_user", + "EMAIL_HOST_PASSWORD=$$secret_email_host_password", + "EMAIL_USE_TLS=$$config_email_use_tls", + "EMAIL_USE_SSL=$$config_email_use_ssl", + "EMAIL_BACKEND=$$config_email_backend", + "MAILGUN_API_KEY=$$secret_mailgun_api_key", + "SENDGRID_API_KEY=$$secret_sendgrid_api_key", + "ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration", + "DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email", + "DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password", + "DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username", + ], + "ports": [ + "8000" + ] + }, + "$$id-postgresql": { + "name": "PostgreSQL", + "depends_on": [], + "image": "postgres:12-alpine", + "volumes": [ + "$$id-postgresql-data:/var/lib/postgresql/data", + ], + "environment": [ + "POSTGRES_USER=$$config_postgres_user", + "POSTGRES_PASSWORD=$$secret_postgres_password", + "POSTGRES_DB=$$config_postgres_db", + ], + "ports": [] + }, + "$$id-redis": { + "name": "Redis", + "depends_on": [], + "image": "redis:7-alpine", + "volumes": [ + "$$id-postgresql-redis-data:/data", + ], + "environment": [], + "ports": [] + } + }, + "variables": [ + { + "id": "$$config_glitchtip_domain", + "name": "GLITCHTIP_DOMAIN", + "label": "GLITCHTIP_DOMAIN URL", + "defaultValue": "$$generate_fqdn", + "description": "", + }, + { + "id": "$$secret_database_url", + "name": "DATABASE_URL", + "label": "Database URL for PostgreSQL", + "defaultValue": "postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db", + "description": "", + }, + { + "id": "$$secret_redis_url", + "name": "REDIS_URL", + "label": "Redis URL", + "defaultValue": "redis://$$id-redis:6379/0", + "description": "", + }, + { + "id": "$$config_default_from_email", + "name": "DEFAULT_FROM_EMAIL", + "label": "Default Email Address", + "defaultValue": "noreply@example.com", + "description": "", + }, + { + "id": "$$config_email_host", + "name": "EMAIL_HOST", + "label": "Email SMTP Host", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_email_port", + "name": "EMAIL_PORT", + "label": "Email SMTP Port", + "defaultValue": "25", + "description": "", + }, + { + "id": "$$config_email_host_user", + "name": "EMAIL_HOST_USER", + "label": "Email SMTP User", + "defaultValue": "", + "description": "", + }, + { + "id": "$$secret_email_host_password", + "name": "EMAIL_HOST_PASSWORD", + "label": "Email SMTP Password", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_email_use_tls", + "name": "EMAIL_USE_TLS", + "label": "Email Use TLS", + "defaultValue": "false", + "description": "", + }, + { + "id": "$$config_email_use_ssl", + "name": "EMAIL_USE_SSL", + "label": "Email Use SSL", + "defaultValue": "false", + "description": "", + }, + { + "id": "$$secret_email_smtp_password", + "name": "EMAIL_SMTP_PASSWORD", + "label": "SMTP Password", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_email_backend", + "name": "EMAIL_BACKEND", + "label": "Email Backend", + "defaultValue": "", + "description": "", + }, + { + "id": "$$secret_mailgun_api_key", + "name": "MAILGUN_API_KEY", + "label": "Mailgun API Key", + "defaultValue": "", + "description": "", + }, + { + "id": "$$secret_sendgrid_api_key", + "name": "SENDGRID_API_KEY", + "label": "Sendgrid API Key", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_enable_open_user_registration", + "name": "ENABLE_OPEN_USER_REGISTRATION", + "label": "Enable Open User Registration", + "defaultValue": "true", + "description": "", + }, + { + "id": "$$config_django_superuser_email", + "name": "DJANGO_SUPERUSER_EMAIL", + "label": "Django Superuser Email", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_django_superuser_username", + "name": "DJANGO_SUPERUSER_USERNAME", + "label": "Django Superuser Username", + "defaultValue": "", + "description": "", + }, + { + "id": "$$secret_django_superuser_password", + "name": "DJANGO_SUPERUSER_PASSWORD", + "label": "Django Superuser Password", + "defaultValue": "$$generate_password", + "description": "", + }, + { + "id": "$$config_postgres_user", + "name": "POSTGRES_USER", + "label": "PostgreSQL User", + "defaultValue": "$$generate_username", + "description": "", + }, + { + "id": "$$secret_postgres_password", + "name": "POSTGRES_PASSWORD", + "label": "PostgreSQL Password", + "defaultValue": "", + "description": "", + }, + { + "id": "$$config_postgres_db", + "name": "POSTGRES_DB", + "label": "PostgreSQL Database", + "defaultValue": "hasura", + "description": "", + }, + ] + }, { "templateVersion": "1.0.0", "serviceDefaultVersion": "v2.13.0", @@ -755,7 +969,7 @@ export default [ "BASE_URL=$$config_base_url", "JWT_SECRET=$$secret_jwt_secret", "EMAIL_NOREPLY=$$config_email_noreply", - "EMAIL_MAILGUN_API_KEY=$$secret_email_mailgun_api_key", + "EMAIL_MAILGUN_API=$$secret_email_mailgun_api", "EMAIL_MAILGUN_REGION=$$config_email_mailgun_region", "EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain", "EMAIL_SMTP_HOST=$$config_email_smtp_host", @@ -813,8 +1027,8 @@ export default [ "description": "", }, { - "id": "$$secret_email_mailgun_api_key", - "name": "EMAIL_MAILGUN_API_KEY", + "id": "$$secret_email_mailgun_api", + "name": "EMAIL_MAILGUN_API", "label": "Mailgun API Key", "defaultValue": "", "description": "", diff --git a/apps/ui/src/routes/services/[id]/__layout.svelte b/apps/ui/src/routes/services/[id]/__layout.svelte index 708b2ab6f..9695f0cfd 100644 --- a/apps/ui/src/routes/services/[id]/__layout.svelte +++ b/apps/ui/src/routes/services/[id]/__layout.svelte @@ -70,6 +70,7 @@ import { onDestroy, onMount } from 'svelte'; import { goto } from '$app/navigation'; import Menu from './_Menu.svelte'; + import { saveForm } from './utils'; const { id } = $page.params; $isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service); @@ -112,6 +113,11 @@ $status.service.initialLoading = true; $status.service.loading = true; try { + const form: any = document.getElementById('saveForm'); + if (form) { + const formData = new FormData(form); + service = await saveForm(formData, service); + } await post(`/services/${service.id}/start`, {}); } catch (error) { return errorNotification(error); diff --git a/apps/ui/src/routes/services/[id]/index.svelte b/apps/ui/src/routes/services/[id]/index.svelte index 64d7ff97a..f944eab51 100644 --- a/apps/ui/src/routes/services/[id]/index.svelte +++ b/apps/ui/src/routes/services/[id]/index.svelte @@ -34,6 +34,7 @@ import DocLink from '$lib/components/DocLink.svelte'; import Explainer from '$lib/components/Explainer.svelte'; import ServiceStatus from '$lib/components/ServiceStatus.svelte'; + import { saveForm } from './utils'; const { id } = $page.params; $: isDisabled = @@ -71,30 +72,9 @@ } async function handleSubmit(e: any) { - const formData = new FormData(e.target); - const settings = service.serviceSetting.map((setting: { name: string }) => setting.name); - const baseCoolifySetting = ['name', 'fqdn', 'exposePort']; - - 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, - name: key, - value: value, - isNew: true - }); - } - } if (loading.save) return; loading.save = true; + try { // await post(`/services/${id}/check`, { // fqdn: service.fqdn, @@ -103,13 +83,10 @@ // otherFqdns: service.minio?.apiFqdn ? [service.minio?.apiFqdn] : [], // exposePort: service.exposePort // }); - await post(`/services/${id}`, { ...service }); + const formData = new FormData(e.target); + service = await saveForm(formData, service); setLocation(service); - - const reloadServices = await get(`/services/${id}`); - service = reloadServices.service forceSave = false; - $isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service); return addToast({ message: 'Configuration saved.', diff --git a/apps/ui/src/routes/services/[id]/utils.ts b/apps/ui/src/routes/services/[id]/utils.ts index bda896ed3..dc7202381 100644 --- a/apps/ui/src/routes/services/[id]/utils.ts +++ b/apps/ui/src/routes/services/[id]/utils.ts @@ -1,4 +1,4 @@ -import { post } from '$lib/api'; +import { get, post } from '$lib/api'; import { t } from '$lib/translations'; import { errorNotification } from '$lib/common'; @@ -40,3 +40,30 @@ export async function saveSecret({ throw error } } + +export async function saveForm(formData: any, service: any) { + const settings = service.serviceSetting.map((setting: { name: string }) => setting.name); + const baseCoolifySetting = ['name', 'fqdn', 'exposePort']; + 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, + name: key, + value: value, + isNew: true + }); + } + } + await post(`/services/${service.id}`, { ...service }); + const { service: reloadedService } = await get(`/services/${service.id}`); + return reloadedService; + +}