feat: new service - weblate
This commit is contained in:
parent
a667435ef2
commit
40e8dd4a8d
@ -0,0 +1,18 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Weblate" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"adminPassword" TEXT NOT NULL,
|
||||||
|
"postgresqlHost" TEXT NOT NULL,
|
||||||
|
"postgresqlPort" INTEGER NOT NULL,
|
||||||
|
"postgresqlUser" TEXT NOT NULL,
|
||||||
|
"postgresqlPassword" TEXT NOT NULL,
|
||||||
|
"postgresqlDatabase" TEXT NOT NULL,
|
||||||
|
"postgresqlPublicPort" INTEGER,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Weblate_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Weblate_serviceId_key" ON "Weblate"("serviceId");
|
@ -200,7 +200,7 @@ model Build {
|
|||||||
commit String?
|
commit String?
|
||||||
pullmergeRequestId String?
|
pullmergeRequestId String?
|
||||||
forceRebuild Boolean @default(false)
|
forceRebuild Boolean @default(false)
|
||||||
sourceBranch String?
|
sourceBranch String?
|
||||||
branch String?
|
branch String?
|
||||||
status String? @default("queued")
|
status String? @default("queued")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@ -348,6 +348,7 @@ model Service {
|
|||||||
wordpress Wordpress?
|
wordpress Wordpress?
|
||||||
appwrite Appwrite?
|
appwrite Appwrite?
|
||||||
searxng Searxng?
|
searxng Searxng?
|
||||||
|
weblate Weblate?
|
||||||
}
|
}
|
||||||
|
|
||||||
model PlausibleAnalytics {
|
model PlausibleAnalytics {
|
||||||
@ -559,3 +560,18 @@ model Searxng {
|
|||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
service Service @relation(fields: [serviceId], references: [id])
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Weblate {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
adminPassword String
|
||||||
|
postgresqlHost String
|
||||||
|
postgresqlPort Int
|
||||||
|
postgresqlUser String
|
||||||
|
postgresqlPassword String
|
||||||
|
postgresqlDatabase String
|
||||||
|
postgresqlPublicPort Int?
|
||||||
|
serviceId String @unique
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
}
|
||||||
|
@ -1,23 +1,7 @@
|
|||||||
import { exec } from 'node:child_process'
|
|
||||||
import util from 'util';
|
|
||||||
import fs from 'fs/promises';
|
|
||||||
import yaml from 'js-yaml';
|
|
||||||
import forge from 'node-forge';
|
|
||||||
import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator';
|
|
||||||
import type { Config } from 'unique-names-generator';
|
|
||||||
import generator from 'generate-password';
|
|
||||||
import crypto from 'crypto';
|
|
||||||
import { promises as dns } from 'dns';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import os from 'os';
|
|
||||||
import sshConfig from 'ssh-config'
|
|
||||||
import { encrypt, generatePassword, prisma } from '../common';
|
import { encrypt, generatePassword, prisma } from '../common';
|
||||||
|
|
||||||
|
|
||||||
export const version = '3.8.2';
|
|
||||||
export const isDev = process.env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
export const includeServices: any = {
|
export const includeServices: any = {
|
||||||
destinationDocker: true,
|
destinationDocker: true,
|
||||||
persistentStorage: true,
|
persistentStorage: true,
|
||||||
@ -34,7 +18,8 @@ export const includeServices: any = {
|
|||||||
moodle: true,
|
moodle: true,
|
||||||
appwrite: true,
|
appwrite: true,
|
||||||
glitchTip: true,
|
glitchTip: true,
|
||||||
searxng: true
|
searxng: true,
|
||||||
|
weblate: true
|
||||||
};
|
};
|
||||||
export async function configureServiceType({
|
export async function configureServiceType({
|
||||||
id,
|
id,
|
||||||
@ -312,6 +297,27 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}else if (type === 'weblate') {
|
||||||
|
const adminPassword = encrypt(generatePassword({}))
|
||||||
|
const postgresqlUser = cuid();
|
||||||
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
|
const postgresqlDatabase = 'weblate';
|
||||||
|
await prisma.service.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
weblate: {
|
||||||
|
create: {
|
||||||
|
adminPassword,
|
||||||
|
postgresqlHost: `${id}-postgresql`,
|
||||||
|
postgresqlPort: 5432,
|
||||||
|
postgresqlUser,
|
||||||
|
postgresqlPassword,
|
||||||
|
postgresqlDatabase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -338,7 +344,7 @@ export async function removeService({ id }: { id: string }): Promise<void> {
|
|||||||
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.weblate.deleteMany({ where: { serviceId: id } });
|
||||||
|
|
||||||
await prisma.service.delete({ where: { id } });
|
await prisma.service.delete({ where: { id } });
|
||||||
}
|
}
|
@ -63,6 +63,9 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
if (type === 'searxng') {
|
if (type === 'searxng') {
|
||||||
return await startSearXNGService(request)
|
return await startSearXNGService(request)
|
||||||
}
|
}
|
||||||
|
if (type === 'weblate') {
|
||||||
|
return await startWeblateService(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 }
|
||||||
@ -2224,3 +2227,106 @@ async function startSearXNGService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function startWeblateService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
|
try {
|
||||||
|
const { id } = request.params;
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
|
const {
|
||||||
|
weblate: { adminPassword, postgresqlHost, postgresqlPort, postgresqlUser, postgresqlPassword, postgresqlDatabase }
|
||||||
|
} = service;
|
||||||
|
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort, persistentStorage, fqdn } =
|
||||||
|
service;
|
||||||
|
const network = destinationDockerId && destinationDocker.network;
|
||||||
|
const port = getServiceMainPort('weblate');
|
||||||
|
|
||||||
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
|
const image = getServiceImage(type);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
weblate: {
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
volume: `${id}-data:/app/data`,
|
||||||
|
environmentVariables: {
|
||||||
|
WEBLATE_SITE_DOMAIN: getDomain(fqdn),
|
||||||
|
WEBLATE_ADMIN_PASSWORD: adminPassword,
|
||||||
|
POSTGRES_PASSWORD: postgresqlPassword,
|
||||||
|
POSTGRES_USER: postgresqlUser,
|
||||||
|
POSTGRES_DATABASE: postgresqlDatabase,
|
||||||
|
POSTGRES_HOST: postgresqlHost,
|
||||||
|
POSTGRES_PORT: postgresqlPort,
|
||||||
|
REDIS_HOST: `${id}-redis`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
postgresql: {
|
||||||
|
image: `postgres:14-alpine`,
|
||||||
|
volume: `${id}-postgresql-data:/var/lib/postgresql/data`,
|
||||||
|
environmentVariables: {
|
||||||
|
POSTGRES_PASSWORD: postgresqlPassword,
|
||||||
|
POSTGRES_USER: postgresqlUser,
|
||||||
|
POSTGRES_DB: postgresqlDatabase,
|
||||||
|
POSTGRES_HOST: postgresqlHost,
|
||||||
|
POSTGRES_PORT: postgresqlPort,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
image: `redis:6-alpine`,
|
||||||
|
volume: `${id}-redis-data:/data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (serviceSecret.length > 0) {
|
||||||
|
serviceSecret.forEach((secret) => {
|
||||||
|
config.weblate.environmentVariables[secret.name] = secret.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { volumes, volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
|
const composeFile: ComposeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[id]: {
|
||||||
|
container_name: id,
|
||||||
|
image: config.weblate.image,
|
||||||
|
environment: config.weblate.environmentVariables,
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
volumes,
|
||||||
|
labels: makeLabelForServices('weblate'),
|
||||||
|
...defaultComposeConfiguration(network),
|
||||||
|
},
|
||||||
|
[`${id}-postgresql`]: {
|
||||||
|
container_name: `${id}-postgresql`,
|
||||||
|
image: config.postgresql.image,
|
||||||
|
environment: config.postgresql.environmentVariables,
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
volumes,
|
||||||
|
labels: makeLabelForServices('weblate'),
|
||||||
|
...defaultComposeConfiguration(network),
|
||||||
|
},
|
||||||
|
[`${id}-redis`]: {
|
||||||
|
container_name: `${id}-redis`,
|
||||||
|
image: config.redis.image,
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
volumes,
|
||||||
|
labels: makeLabelForServices('weblate'),
|
||||||
|
...defaultComposeConfiguration(network),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: volumeMounts
|
||||||
|
};
|
||||||
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
|
await startServiceContainers(destinationDocker.id, composeFileDestination)
|
||||||
|
return {}
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -599,6 +599,54 @@ export const glitchTip = [{
|
|||||||
isBoolean: false,
|
isBoolean: false,
|
||||||
isEncrypted: true
|
isEncrypted: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpHost',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpPassword',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpUseSsl',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: true,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpUseSsl',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: true,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpPort',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: true,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailSmtpUser',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'defaultEmail',
|
name: 'defaultEmail',
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
@ -624,7 +672,7 @@ export const glitchTip = [{
|
|||||||
isEncrypted: true
|
isEncrypted: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'defaultFromEmail',
|
name: 'defaultEmailFrom',
|
||||||
isEditable: true,
|
isEditable: true,
|
||||||
isLowerCase: false,
|
isLowerCase: false,
|
||||||
isNumber: false,
|
isNumber: false,
|
||||||
@ -687,4 +735,53 @@ export const searxng = [{
|
|||||||
isNumber: false,
|
isNumber: false,
|
||||||
isBoolean: false,
|
isBoolean: false,
|
||||||
isEncrypted: true
|
isEncrypted: true
|
||||||
|
}]
|
||||||
|
|
||||||
|
export const weblate = [{
|
||||||
|
name: 'adminPassword',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'postgresqlHost',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'postgresqlPort',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
}]
|
}]
|
@ -190,4 +190,15 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
main: 8080
|
main: 8080
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'weblate',
|
||||||
|
fancyName: 'Weblate',
|
||||||
|
baseImage: 'weblate/weblate',
|
||||||
|
images: ['postgres:14-alpine','redis:6-alpine'],
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
@ -40,4 +40,6 @@
|
|||||||
<Icons.GlitchTip {isAbsolute} />
|
<Icons.GlitchTip {isAbsolute} />
|
||||||
{:else if type === 'searxng'}
|
{:else if type === 'searxng'}
|
||||||
<Icons.Searxng {isAbsolute} />
|
<Icons.Searxng {isAbsolute} />
|
||||||
|
{:else if type === 'weblate'}
|
||||||
|
<Icons.Weblate {isAbsolute} />
|
||||||
{/if}
|
{/if}
|
||||||
|
61
apps/ui/src/lib/components/svg/services/Weblate.svelte
Normal file
61
apps/ui/src/lib/components/svg/services/Weblate.svelte
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let isAbsolute = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class={isAbsolute ? 'w-16 h-16 absolute top-0 left-0 -m-7' : 'w-12 h-12 mx-auto'}
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 300 300"
|
||||||
|
><linearGradient
|
||||||
|
id="a"
|
||||||
|
x1=".3965"
|
||||||
|
x2="98.808"
|
||||||
|
y1="55.253"
|
||||||
|
y2="55.253"
|
||||||
|
gradientTransform="scale(.98308 1.0172)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-color="#00d2e6" offset="0" /><stop
|
||||||
|
stop-color="#2eccaa"
|
||||||
|
offset="1"
|
||||||
|
/></linearGradient
|
||||||
|
><linearGradient
|
||||||
|
id="b"
|
||||||
|
x1="49.017"
|
||||||
|
x2="99.793"
|
||||||
|
y1="137.89"
|
||||||
|
y2="113.96"
|
||||||
|
gradientTransform="scale(1.1631 .8598)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-opacity="0" offset="0" /><stop offset=".51413" /><stop
|
||||||
|
stop-opacity="0"
|
||||||
|
offset="1"
|
||||||
|
/></linearGradient
|
||||||
|
><linearGradient
|
||||||
|
id="c"
|
||||||
|
x1="201.82"
|
||||||
|
x2="103.58"
|
||||||
|
y1="57.649"
|
||||||
|
y2="57.649"
|
||||||
|
gradientTransform="scale(.98308 1.0172)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-color="#1fa385" offset="0" /><stop
|
||||||
|
stop-color="#2eccaa"
|
||||||
|
offset="1"
|
||||||
|
/></linearGradient
|
||||||
|
><g transform="translate(50,76)" fill-rule="evenodd"
|
||||||
|
><path
|
||||||
|
d="m127.25 111.61c-2.8884-0.0145-5.7666-0.6024-8.4797-1.7847-6.1117-2.6626-11.493-7.6912-15.872-14.495 1.2486-2.2193 2.3738-4.5173 3.3784-6.8535 4.4051-10.243 6.5-21.46 6.6607-32.593-0.0233-0.22082-0.0416-0.44244-0.0552-0.66483l-0.0121-0.57132c-0.01-4.3654-0.67459-8.7898-2.1767-12.909-1.7304-4.7458-4.4887-9.4955-8.865-11.348-0.79519-0.33595-1.6316-0.47701-2.4642-0.45737-5.5049-10.289-5.6799-20.149 0-29.537 0.10115 0 0.20619 3.9293e-4 0.30734 0.001179 6.7012 0.07387 13.34 2.1418 19.021 5.7536 15.469 9.835 23.182 29.001 23.352 47.818 2e-3 0.22083-3.9e-4 0.44126-7e-3 0.66169h0.0868c-0.0226 19.887-4.8049 40.054-14.875 56.979zm-34.3 31.216c-14.448 5.9425-31.228 5.6236-45.549-1.025-16.476-7.6476-29.065-22.512-36.818-39.479-13.262-29.022-13.566-63.715-0.98815-93.182 9.4458 3.7788 17.845-2.2397 17.845-2.2397s-0.01945 9.2605 8.9478 13.905c-9.2007 21.556-8.979 47.167 0.2412 68.173 4.4389 10.107 11.22 19.519 20.619 24.842 3.3547 1.8996 7.041 3.126 10.833 3.5862 0.01404 0.0219 0.02808 0.0439 0.04214 0.0658 6.6965 10.449 15.132 19.157 24.828 25.354z"
|
||||||
|
fill="url(#a)"
|
||||||
|
fill-rule="nonzero"
|
||||||
|
/><path
|
||||||
|
d="m127.24 111.61c-2.8869-0.0151-5.7636-0.60296-8.4755-1.7846-6.1127-2.663-11.495-7.6928-15.874-14.498 1.2494-2.2205 2.3754-4.5198 3.3806-6.8572 1.3282-3.0884 2.4463-6.2648 3.3644-9.501 2.128-7.4978 30.382 2.0181 26.072 14.371-2.2239 6.373-5.0394 12.509-8.4675 18.27zm-34.302 31.212c-14.446 5.9396-31.224 5.6198-45.543-1.0278-16.476-7.6476 0.44739-33.303 9.8465-27.981 3.3533 1.8988 7.0378 3.125 10.828 3.5856 0.01567 0.0245 0.03135 0.049 0.04704 0.0735 6.695 10.447 15.128 19.153 24.821 25.349z"
|
||||||
|
fill="url(#b)"
|
||||||
|
opacity=".3"
|
||||||
|
/><path
|
||||||
|
d="m56.762 54.628c-0.0066-0.22043-0.0093-0.44086-7e-3 -0.66169 0.17001-18.817 7.8827-37.983 23.352-47.818 5.6811-3.6118 12.32-5.6798 19.021-5.7536 0.10115-7.8585e-4 0.20619-0.001179 0.30734-0.001179v29.537c-0.83254-0.01965-1.669 0.12141-2.4642 0.45737-4.3763 1.8523-7.1345 6.602-8.865 11.348-1.5021 4.1191-2.1669 8.5434-2.1767 12.909l-0.01206 0.57132c-0.01362 0.2224-0.0319 0.44401-0.05524 0.66483 0.16067 11.134 2.2556 22.35 6.6607 32.593 4.9334 11.472 12.775 22.025 23.847 26.849 8.3526 3.6397 17.612 2.7811 25.182-1.5057 9.3991-5.3226 16.18-14.734 20.619-24.842 9.2202-21.006 9.4419-46.617 0.24121-68.173 8.9673-4.6444 8.9478-13.905 8.9478-13.905s8.3993 6.0185 17.845 2.2397c12.578 29.466 12.274 64.16-0.98815 93.182-7.7535 16.967-20.343 31.831-36.818 39.479-14.667 6.809-31.913 6.9792-46.591 0.58389-13.19-5.7489-23.918-16.106-31.637-28.15-11.179-17.443-16.472-38.678-16.496-59.604z"
|
||||||
|
fill="url(#c)"
|
||||||
|
fill-rule="nonzero"
|
||||||
|
/></g
|
||||||
|
></svg
|
||||||
|
>
|
@ -16,4 +16,5 @@ export { default as Fider } from './Fider.svelte';
|
|||||||
export { default as Appwrite } from './Appwrite.svelte';
|
export { default as Appwrite } from './Appwrite.svelte';
|
||||||
export { default as Moodle } from './Moodle.svelte';
|
export { default as Moodle } from './Moodle.svelte';
|
||||||
export { default as GlitchTip } from './GlitchTip.svelte';
|
export { default as GlitchTip } from './GlitchTip.svelte';
|
||||||
export { default as Searxng } from './Searxng.svelte';
|
export { default as Searxng } from './Searxng.svelte';
|
||||||
|
export { default as Weblate } from './Weblate.svelte';
|
@ -71,4 +71,8 @@
|
|||||||
<a href="https://searxng.org" target="_blank">
|
<a href="https://searxng.org" target="_blank">
|
||||||
<Icons.Searxng />
|
<Icons.Searxng />
|
||||||
</a>
|
</a>
|
||||||
|
{:else if service.type === 'weblate'}
|
||||||
|
<a href="https://weblate.org" target="_blank">
|
||||||
|
<Icons.Weblate />
|
||||||
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
import Appwrite from './_Appwrite.svelte';
|
import Appwrite from './_Appwrite.svelte';
|
||||||
import Moodle from './_Moodle.svelte';
|
import Moodle from './_Moodle.svelte';
|
||||||
import Searxng from './_Searxng.svelte';
|
import Searxng from './_Searxng.svelte';
|
||||||
|
import Weblate from './_Weblate.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
$: isDisabled =
|
$: isDisabled =
|
||||||
@ -405,6 +406,8 @@
|
|||||||
<GlitchTip bind:service />
|
<GlitchTip bind:service />
|
||||||
{:else if service.type === 'searxng'}
|
{:else if service.type === 'searxng'}
|
||||||
<Searxng bind:service />
|
<Searxng bind:service />
|
||||||
|
{:else if service.type === 'weblate'}
|
||||||
|
<Weblate bind:service />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
66
apps/ui/src/routes/services/[id]/_Services/_Weblate.svelte
Normal file
66
apps/ui/src/routes/services/[id]/_Services/_Weblate.svelte
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
export let service: any;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">Weblate</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="adminPassword">Admin password</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="adminPassword"
|
||||||
|
id="adminPassword"
|
||||||
|
isPasswordField
|
||||||
|
value={service.weblate.adminPassword}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</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="postgresqlHost">PostgreSQL Host</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="postgresqlHost"
|
||||||
|
id="postgresqlHost"
|
||||||
|
value={service.weblate.postgresqlHost}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="postgresqlPort">PostgreSQL Port</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="postgresqlPort"
|
||||||
|
id="postgresqlPort"
|
||||||
|
value={service.weblate.postgresqlPort}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="postgresqlUser">PostgreSQL User</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="postgresqlUser"
|
||||||
|
id="postgresqlUser"
|
||||||
|
value={service.weblate.postgresqlUser}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="postgresqlPassword">PostgreSQL Password</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="postgresqlPassword"
|
||||||
|
id="postgresqlPassword"
|
||||||
|
isPasswordField
|
||||||
|
value={service.weblate.postgresqlPassword}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user