feat: add GlitchTip service

This commit is contained in:
Guillaume Bonnet 2022-08-15 09:56:34 +00:00
parent 0745a12e7d
commit 26d0ef9ac9
13 changed files with 745 additions and 5 deletions

View File

@ -129,6 +129,7 @@ ### Services
- [Umami](https://github.com/mikecao/umami) - [Umami](https://github.com/mikecao/umami)
- [Fider](https://fider.io) - [Fider](https://fider.io)
- [Hasura](https://hasura.io) - [Hasura](https://hasura.io)
- [GlitchTip](https://glitchtip.com)
If you have a new service you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community! If you have a new service you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!

View File

@ -0,0 +1,30 @@
-- CreateTable
CREATE TABLE "GlitchTip" (
"id" TEXT NOT NULL PRIMARY KEY,
"postgresqlUser" TEXT NOT NULL,
"postgresqlPassword" TEXT NOT NULL,
"postgresqlDatabase" TEXT NOT NULL,
"postgresqlPublicPort" INTEGER,
"secretKeyBase" TEXT,
"defaultEmail" TEXT NOT NULL,
"defaultUsername" TEXT NOT NULL,
"defaultPassword" TEXT NOT NULL,
"defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl',
"emailSmtpHost" TEXT DEFAULT 'domain.tdl',
"emailSmtpPort" INTEGER DEFAULT 25,
"emailSmtpUser" TEXT,
"emailSmtpPassword" TEXT,
"emailSmtpUseTls" BOOLEAN DEFAULT false,
"emailSmtpUseSsl" BOOLEAN DEFAULT false,
"emailBackend" TEXT,
"mailgunApiKey" TEXT,
"sendgridApiKey" TEXT,
"enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true,
"serviceId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId");

View File

@ -325,6 +325,7 @@ model Service {
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
fider Fider? fider Fider?
ghost Ghost? ghost Ghost?
glitchTip GlitchTip?
hasura Hasura? hasura Hasura?
meiliSearch MeiliSearch? meiliSearch MeiliSearch?
minio Minio? minio Minio?
@ -491,3 +492,30 @@ model Moodle {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
service Service @relation(fields: [serviceId], references: [id]) service Service @relation(fields: [serviceId], references: [id])
} }
model GlitchTip {
id String @id @default(cuid())
postgresqlUser String
postgresqlPassword String
postgresqlDatabase String
postgresqlPublicPort Int?
secretKeyBase String?
defaultEmail String
defaultUsername String
defaultPassword String
defaultEmailFrom String @default("glitchtip@domain.tdl")
emailSmtpHost String? @default("domain.tdl")
emailSmtpPort Int? @default(25)
emailSmtpUser String?
emailSmtpPassword String?
emailSmtpUseTls Boolean? @default(false)
emailSmtpUseSsl Boolean? @default(false)
emailBackend String?
mailgunApiKey String?
sendgridApiKey String?
enableOpenUserRegistration Boolean @default(true)
serviceId String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
service Service @relation(fields: [serviceId], references: [id])
}

View File

@ -78,6 +78,7 @@ export const include: any = {
umami: true, umami: true,
hasura: true, hasura: true,
fider: true, fider: true,
glitchTip: true,
}; };
export const uniqueName = (): string => uniqueNamesGenerator(customConfig); export const uniqueName = (): string => uniqueNamesGenerator(customConfig);
@ -280,6 +281,17 @@ export const supportedServiceTypesAndVersions = [
// main: 8080 // main: 8080
// } // }
// } // }
{
name: 'glitchTip',
fancyName: 'GlitchTip',
baseImage: 'glitchtip/glitchtip',
images: ['postgres:14-alpine', 'redis:7-alpine'],
versions: ['latest'],
recommendedVersion: 'latest',
ports: {
main: 8000
}
},
]; ];
export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> { export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> {
@ -1517,7 +1529,33 @@ export async function configureServiceType({
} }
} }
}); });
} else { } else if (type === 'glitchTip') {
const defaultUsername = cuid();
const defaultEmail = `${defaultUsername}@example.com`;
const defaultPassword = encrypt(generatePassword());
const postgresqlUser = cuid();
const postgresqlPassword = encrypt(generatePassword());
const postgresqlDatabase = 'glitchTip';
const secretKeyBase = encrypt(generatePassword(64));
await prisma.service.update({
where: { id },
data: {
type,
glitchTip: {
create: {
postgresqlDatabase,
postgresqlUser,
postgresqlPassword,
secretKeyBase,
defaultEmail,
defaultUsername,
defaultPassword,
}
}
}
});
} else {
await prisma.service.update({ await prisma.service.update({
where: { id }, where: { id },
data: { data: {
@ -1538,6 +1576,7 @@ export async function removeService({ id }: { id: string }): Promise<void> {
await prisma.minio.deleteMany({ where: { serviceId: id } }); await prisma.minio.deleteMany({ where: { serviceId: id } });
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } }); await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
await prisma.wordpress.deleteMany({ where: { serviceId: id } }); await prisma.wordpress.deleteMany({ where: { serviceId: id } });
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } }); await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
await prisma.service.delete({ where: { id } }); await prisma.service.delete({ where: { id } });

View File

@ -476,4 +476,116 @@ export const moodle = [{
isNumber: false, isNumber: false,
isBoolean: false, isBoolean: false,
isEncrypted: false isEncrypted: false
}]
export const glitchTip = [{
name: 'postgresqlUser',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'postgresqlPassword',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: true
},
{
name: 'postgresqlDatabase',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'postgresqlPublicPort',
isEditable: false,
isLowerCase: false,
isNumber: true,
isBoolean: false,
isEncrypted: false
},
{
name: 'secretKeyBase',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: true
},
{
name: 'defaultEmail',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'defaultUsername',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'defaultPassword',
isEditable: false,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: true
},
{
name: 'defaultFromEmail',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'emailUrl',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'emailBackend',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: false
},
{
name: 'mailgunApiKey',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: true
},
{
name: 'sendgridApiKey',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: false,
isEncrypted: true
},
{
name: 'enableOpenUserRegistration',
isEditable: true,
isLowerCase: false,
isNumber: false,
isBoolean: true,
isEncrypted: false
}] }]

View File

@ -590,6 +590,9 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
if (type === 'moodle') { if (type === 'moodle') {
return await startMoodleService(request) return await startMoodleService(request)
} }
if (type === 'glitchTip') {
return await startGlitchTipService(request)
}
throw `Service type ${type} not supported.` throw `Service type ${type} not supported.`
} catch (error) { } catch (error) {
throw { status: 500, message: error?.message || error } throw { status: 500, message: error?.message || error }
@ -643,6 +646,9 @@ export async function stopService(request: FastifyRequest<ServiceStartStop>) {
if (type === 'moodle') { if (type === 'moodle') {
return await stopMoodleService(request) return await stopMoodleService(request)
} }
if (type === 'glitchTip') {
return await stopGlitchTipService(request)
}
throw `Service type ${type} not supported.` throw `Service type ${type} not supported.`
} catch (error) { } catch (error) {
throw { status: 500, message: error?.message || error } throw { status: 500, message: error?.message || error }
@ -1247,7 +1253,7 @@ async function startWordpressService(request: FastifyRequest<ServiceStartStop>)
const { volumes, volumeMounts } = persistentVolumes(id, persistentStorage, config.wordpress) const { volumes, volumeMounts } = persistentVolumes(id, persistentStorage, config.wordpress)
let composeFile: ComposeFile = { const composeFile: ComposeFile = {
version: '3.8', version: '3.8',
services: { services: {
[id]: { [id]: {
@ -2633,6 +2639,252 @@ async function stopMoodleService(request: FastifyRequest<ServiceStartStop>) {
} }
} }
async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>) {
try {
const { id } = request.params;
const teamId = request.user.teamId;
const service = await getServiceFromDB({ id, teamId });
const {
type,
version,
fqdn,
destinationDockerId,
destinationDocker,
serviceSecret,
persistentStorage,
exposePort,
glitchTip: {
postgresqlDatabase,
postgresqlPassword,
postgresqlUser,
secretKeyBase,
defaultEmail,
defaultUsername,
defaultPassword,
defaultFromEmail,
emailSmtpHost,
emailSmtpPort,
emailSmtpUser,
emailSmtpPassword,
emailSmtpUseTls,
emailSmtpUseSsl,
emailBackend,
mailgunApiKey,
sendgridApiKey,
enableOpenUserRegistration,
}
} = service;
const network = destinationDockerId && destinationDocker.network;
const port = getServiceMainPort('glitchTip');
const { workdir } = await createDirectories({ repository: type, buildId: id });
const image = getServiceImage(type);
const config = {
glitchTip: {
image: `${image}:${version}`,
environmentVariables: {
PORT: port,
GLITCHTIP_DOMAIN: fqdn,
SECRET_KEY: secretKeyBase,
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}`,
REDIS_URL: `redis://${id}-redis:6379/0`,
DEFAULT_FROM_EMAIL: defaultFromEmail,
EMAIL_HOST: emailSmtpHost,
EMAIL_PORT: emailSmtpPort,
EMAIL_HOST_USER: emailSmtpUser,
EMAIL_HOST_PASSWORD: emailSmtpPassword,
EMAIL_USE_TLS: emailSmtpUseTls,
EMAIL_USE_SSL: emailSmtpUseSsl,
EMAIL_BACKEND: emailBackend,
MAILGUN_API_KEY: mailgunApiKey,
SENDGRID_API_KEY: sendgridApiKey,
ENABLE_OPEN_USER_REGISTRATION: enableOpenUserRegistration,
DJANGO_SUPERUSER_EMAIL: defaultEmail,
DJANGO_SUPERUSER_USERNAME: defaultUsername,
DJANGO_SUPERUSER_PASSWORD: defaultPassword,
}
},
postgresql: {
image: 'postgres:14-alpine',
volume: `${id}-postgresql-data:/var/lib/postgresql/data`,
environmentVariables: {
POSTGRES_USER: postgresqlUser,
POSTGRES_PASSWORD: postgresqlPassword,
POSTGRES_DB: postgresqlDatabase
}
},
redis: {
image: 'redis:7-alpine',
volume: `${id}-redis-data:/data`,
}
};
if (serviceSecret.length > 0) {
serviceSecret.forEach((secret) => {
config.glitchTip.environmentVariables[secret.name] = secret.value;
});
}
const { volumes, volumeMounts } = persistentVolumes(id, persistentStorage, config.glitchTip)
const composeFile: ComposeFile = {
version: '3.8',
services: {
[id]: {
container_name: id,
image: config.glitchTip.image,
environment: config.glitchTip.environmentVariables,
networks: [network],
volumes,
restart: 'always',
labels: makeLabelForServices('glitchTip'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
},
depends_on: [`${id}-postgresql`, `${id}-redis`]
},
[`${id}-worker`]: {
container_name: `${id}-worker`,
image: config.glitchTip.image,
command: './bin/run-celery-with-beat.sh',
environment: config.glitchTip.environmentVariables,
networks: [network],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
},
depends_on: [`${id}-postgresql`, `${id}-redis`]
},
[`${id}-setup`]: {
container_name: `${id}-setup`,
image: config.glitchTip.image,
command: 'sh -c "(./manage.py migrate || true) && (./manage.py createsuperuser --noinput || true)"',
environment: config.glitchTip.environmentVariables,
networks: [network],
restart: "no",
depends_on: [`${id}-postgresql`, `${id}-redis`]
},
[`${id}-postgresql`]: {
image: config.postgresql.image,
container_name: `${id}-postgresql`,
environment: config.postgresql.environmentVariables,
networks: [network],
volumes: [config.postgresql.volume],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
}
},
[`${id}-redis`]: {
image: config.redis.image,
container_name: `${id}-redis`,
networks: [network],
volumes: [config.redis.volume],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
}
}
},
networks: {
[network]: {
external: true
}
},
volumes: {
...volumeMounts,
[config.postgresql.volume.split(':')[0]]: {
name: config.postgresql.volume.split(':')[0]
},
[config.redis.volume.split(':')[0]]: {
name: config.redis.volume.split(':')[0]
}
}
};
const composeFileDestination = `${workdir}/docker-compose.yaml`;
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} pull` })
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} up --build -d` })
return {}
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
async function stopGlitchTipService(request: FastifyRequest<ServiceStartStop>) {
try {
const { id } = request.params;
const teamId = request.user.teamId;
const service = await getServiceFromDB({ id, teamId });
const { destinationDockerId, destinationDocker } = service;
if (destinationDockerId) {
try {
const found = await checkContainer({ dockerId: destinationDocker.id, container: id });
if (found) {
await removeContainer({ id, dockerId: destinationDocker.id });
}
} catch (error) {
console.error(error);
}
try {
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-worker` });
if (found) {
await removeContainer({ id: `${id}-worker`, dockerId: destinationDocker.id });
}
} catch (error) {
console.error(error);
}
try {
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-setup` });
if (found) {
await removeContainer({ id: `${id}-setup`, dockerId: destinationDocker.id });
}
} catch (error) {
console.error(error);
}
try {
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-postgresql` });
if (found) {
await removeContainer({ id: `${id}-postgresql`, dockerId: destinationDocker.id });
}
} catch (error) {
console.error(error);
}
try {
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-redis` });
if (found) {
await removeContainer({ id: `${id}-redis`, dockerId: destinationDocker.id });
}
} catch (error) {
console.error(error);
}
}
return {}
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
export async function activatePlausibleUsers(request: FastifyRequest<OnlyId>, reply: FastifyReply) { export async function activatePlausibleUsers(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
try { try {

View File

@ -159,6 +159,17 @@ export const supportedServiceTypesAndVersions = [
// main: 8080 // main: 8080
// } // }
// } // }
{
name: 'glitchTip',
fancyName: 'GlitchTip',
baseImage: 'glitchtip/glitchtip',
images: ['postgres:14-alpine', 'redis:7-alpine'],
versions: ['latest'],
recommendedVersion: 'latest',
ports: {
main: 8000
}
},
]; ];
export const asyncSleep = (delay: number) => export const asyncSleep = (delay: number) =>

View File

@ -0,0 +1,51 @@
<script lang="ts">
export let isAbsolute = false;
</script>
<svg
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate"
viewBox="0 0 400 400"
>
<defs>
<clipPath id="_clipPath_5kOQy2sGcuF9aeG3NHWmCAGgMEPQrnNW">
<rect width="400" height="400" />
</clipPath>
</defs>
<g clip-path="url(#_clipPath_5kOQy2sGcuF9aeG3NHWmCAGgMEPQrnNW)">
<g>
<g>
<path
d=" M 276.155 367.684 L 337.655 367.684 L 337.655 180.781 L 205.525 180.781 L 205.525 241.801 L 267.987 241.801 L 267.987 258.617 C 267.987 291.29 238.678 308.586 202.162 308.586 C 156.998 308.586 127.689 282.641 127.689 226.906 L 127.689 173.094 C 127.689 117.359 156.998 91.414 202.162 91.414 C 241.08 91.414 261.74 112.554 271.83 138.5 L 331.409 104.386 C 306.424 52.976 261.74 26.55 202.162 26.55 C 111.353 26.55 50.333 88.531 50.333 201.441 C 50.333 313.872 110.873 373.45 187.748 373.45 C 238.197 373.45 268.947 347.985 273.752 314.352 L 276.155 314.352 L 276.155 367.684 Z "
fill="rgb(132,24,128)"
/>
</g>
<g opacity="0.5">
<path
d=" M 139.701 175.78 L 139.701 173.094 C 139.701 117.359 169.01 91.414 214.174 91.414 C 253.092 91.414 273.752 112.554 283.842 138.5 L 343.421 104.386 C 318.436 52.976 273.752 26.55 214.174 26.55 C 128.962 26.55 69.981 81.125 63.033 181.145 L 139.701 175.78 Z "
fill-rule="evenodd"
fill="rgb(233,64,86)"
/>
</g>
<g opacity="0.5">
<path
d=" M 349.667 305.194 L 349.667 247.137 L 279.998 252.019 L 279.998 258.617 C 279.998 291.29 250.69 308.586 214.174 308.586 C 179.697 308.586 154.459 293.467 144.446 261.518 L 70.341 266.711 C 76.285 288.796 85.348 307.563 96.86 322.909 L 349.667 305.194 Z "
fill-rule="evenodd"
fill="rgb(233,64,86)"
/>
</g>
<path
d=" M 337.655 247.03 L 337.655 180.781 L 205.525 180.781 L 205.525 241.801 L 267.987 241.801 L 267.987 251.912 L 337.655 247.03 Z M 132.401 261.413 C 129.319 251.534 127.689 240.048 127.689 226.906 L 127.689 175.099 L 51.069 180.468 C 50.581 187.25 50.333 194.242 50.333 201.441 C 50.333 225.632 53.136 247.376 58.301 266.606 L 132.401 261.413 Z "
fill-rule="evenodd"
fill="rgb(233,64,86)"
/>
<path
d=" M 337.655 305.862 L 337.655 367.684 L 276.155 367.684 L 276.155 314.352 L 273.752 314.352 C 268.947 347.985 238.197 373.45 187.748 373.45 C 146.712 373.45 110.33 356.473 85.327 323.543 L 337.655 305.862 Z "
fill-rule="evenodd"
fill="rgb(255,63,42)"
/>
</g>
</g>
</svg>

View File

@ -34,4 +34,6 @@
<Icons.Fider {isAbsolute} /> <Icons.Fider {isAbsolute} />
{:else if type === 'moodle'} {:else if type === 'moodle'}
<Icons.Moodle {isAbsolute} /> <Icons.Moodle {isAbsolute} />
{:else if type === 'glitchTip'}
<Icons.GlitchTip {isAbsolute} />
{/if} {/if}

View File

@ -14,4 +14,4 @@ export { default as Umami } from './Umami.svelte';
export { default as Hasura } from './Hasura.svelte'; export { default as Hasura } from './Hasura.svelte';
export { default as Fider } from './Fider.svelte'; export { default as Fider } from './Fider.svelte';
export { default as Moodle } from './Moodle.svelte'; export { default as Moodle } from './Moodle.svelte';
export { default as GlitchTip } from './GlitchTip.svelte';

View File

@ -59,5 +59,8 @@
<a href="https://moodle.org" target="_blank"> <a href="https://moodle.org" target="_blank">
<Icons.Moodle /> <Icons.Moodle />
</a> </a>
{:else if service.type === 'glitchTip'}
<a href="https://glitchtip.com" target="_blank">
<Icons.GlitchTip />
</a>
{/if} {/if}

View File

@ -0,0 +1,208 @@
<script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import Setting from '$lib/components/Setting.svelte';
import { t } from '$lib/translations';
export let service: any;
function toggleEmailSmtpUseTls() {
service.glitchTip.emailSmtpUseTls = !service.glitchTip.emailSmtpUseTls;
}
function toggleEmailSmtpUseSsl() {
service.glitchTip.emailSmtpUseSsl = !service.glitchTip.emailSmtpUseSsl;
}
function toggleEnableOpenUserRegistration() {
service.glitchTip.enableOpenUserRegistration = !service.glitchTip.enableOpenUserRegistration;
}
</script>
<div class="flex space-x-1 py-5 font-bold">
<div class="title">GlitchTip</div>
</div>
<div class="flex space-x-1 py-2 font-bold">
<div class="subtitle">Settings</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<Setting
bind:setting={service.glitchTip.enableOpenUserRegistration}
on:click={toggleEnableOpenUserRegistration}
title={'Enable Open User Registration'}
description={''}
/>
</div>
<div class="flex space-x-1 py-2 font-bold">
<div class="subtitle">Email settings</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="defaultEmailFrom" class="text-base font-bold text-stone-100">Default Email From</label
>
<CopyPasswordField
required
name="defaultEmailFrom"
id="defaultEmailFrom"
value={service.glitchTip.defaultEmailFrom}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="emailSmtpHost" class="text-base font-bold text-stone-100">SMTP Host</label>
<CopyPasswordField
name="emailSmtpHost"
id="emailSmtpHost"
value={service.glitchTip.emailSmtpHost}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="emailSmtpPort" class="text-base font-bold text-stone-100">SMTP Port</label>
<CopyPasswordField
name="emailSmtpPort"
id="emailSmtpPort"
value={service.glitchTip.emailSmtpPort}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="emailSmtpUser" class="text-base font-bold text-stone-100">SMTP User</label>
<CopyPasswordField
name="emailSmtpUser"
id="emailSmtpUser"
value={service.glitchTip.emailSmtpUser}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="emailSmtpPassword" class="text-base font-bold text-stone-100">SMTP Password</label>
<CopyPasswordField
name="emailSmtpPassword"
id="emailSmtpPassword"
value={service.glitchTip.emailSmtpPassword}
isPasswordField
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<Setting
bind:setting={service.glitchTip.emailSmtpUseTls}
on:click={toggleEmailSmtpUseTls}
title={'SMTP Use TLS'}
description={''}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<Setting
bind:setting={service.glitchTip.emailSmtpUseSsl}
on:click={toggleEmailSmtpUseSsl}
title={'SMTP Use SSL'}
description={''}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="emailBackend" class="text-base font-bold text-stone-100">Email Backend</label>
<CopyPasswordField name="emailBackend" id="emailBackend" value={service.glitchTip.emailBackend} />
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mailgunApiKey" class="text-base font-bold text-stone-100">Mailgun API Key</label>
<CopyPasswordField
name="mailgunApiKey"
id="mailgunApiKey"
value={service.glitchTip.mailgunApiKey}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="sendgridApiKey" class="text-base font-bold text-stone-100">SendGrid API Key</label>
<CopyPasswordField
name="sendgridApiKey"
id="sendgridApiKey"
value={service.glitchTip.sendgridApiKey}
/>
</div>
<div class="flex space-x-1 py-2 font-bold">
<div class="subtitle">Default User & Superuser</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="defaultEmail" class="text-base font-bold text-stone-100">{$t('forms.email')}</label>
<CopyPasswordField
name="defaultEmail"
id="defaultEmail"
value={service.glitchTip.defaultEmail}
readonly
disabled
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="defaultUsername" class="text-base font-bold text-stone-100"
>{$t('forms.username')}</label
>
<CopyPasswordField
name="defaultUsername"
id="defaultUsername"
value={service.glitchTip.defaultUsername}
readonly
disabled
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="defaultPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label
>
<CopyPasswordField
name="defaultPassword"
id="defaultPassword"
value={service.glitchTip.defaultPassword}
readonly
disabled
isPasswordField
/>
</div>
<div class="flex space-x-1 py-5 font-bold">
<div class="title">PostgreSQL</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="postgresqlUser" class="text-base font-bold text-stone-100"
>{$t('forms.username')}</label
>
<CopyPasswordField
name="postgresqlUser"
id="postgresqlUser"
value={service.glitchTip.postgresqlUser}
readonly
disabled
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="postgresqlPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label
>
<CopyPasswordField
id="postgresqlPassword"
isPasswordField
readonly
disabled
name="postgresqlPassword"
value={service.glitchTip.postgresqlPassword}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="postgresqlDatabase" class="text-base font-bold text-stone-100"
>{$t('index.database')}</label
>
<CopyPasswordField
name="postgresqlDatabase"
id="postgresqlDatabase"
value={service.glitchTip.postgresqlDatabase}
readonly
disabled
/>
</div>

View File

@ -19,6 +19,7 @@
import Fider from './_Fider.svelte'; import Fider from './_Fider.svelte';
import Ghost from './_Ghost.svelte'; import Ghost from './_Ghost.svelte';
import GlitchTip from './_GlitchTip.svelte';
import Hasura from './_Hasura.svelte'; import Hasura from './_Hasura.svelte';
import MeiliSearch from './_MeiliSearch.svelte'; import MeiliSearch from './_MeiliSearch.svelte';
import MinIo from './_MinIO.svelte'; import MinIo from './_MinIO.svelte';
@ -37,7 +38,7 @@
save: false, save: false,
verification: false, verification: false,
cleanup: false cleanup: false
} };
let dualCerts = service.dualCerts; let dualCerts = service.dualCerts;
let nonWWWDomain = service.fqdn && getDomain(service.fqdn).replace(/^www\./, ''); let nonWWWDomain = service.fqdn && getDomain(service.fqdn).replace(/^www\./, '');
@ -396,6 +397,8 @@
<Fider bind:service {readOnly} /> <Fider bind:service {readOnly} />
{:else if service.type === 'moodle'} {:else if service.type === 'moodle'}
<Moodle bind:service {readOnly} /> <Moodle bind:service {readOnly} />
{:else if service.type === 'glitchTip'}
<GlitchTip bind:service />
{/if} {/if}
</div> </div>
</form> </form>