feat: new service - weblate

This commit is contained in:
Andras Bacsai
2022-08-31 15:02:05 +02:00
parent a667435ef2
commit 40e8dd4a8d
12 changed files with 413 additions and 22 deletions

View File

@@ -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");

View File

@@ -200,7 +200,7 @@ model Build {
commit String?
pullmergeRequestId String?
forceRebuild Boolean @default(false)
sourceBranch String?
sourceBranch String?
branch String?
status String? @default("queued")
createdAt DateTime @default(now())
@@ -348,6 +348,7 @@ model Service {
wordpress Wordpress?
appwrite Appwrite?
searxng Searxng?
weblate Weblate?
}
model PlausibleAnalytics {
@@ -559,3 +560,18 @@ model Searxng {
updatedAt DateTime @updatedAt
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])
}

View File

@@ -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 os from 'os';
import sshConfig from 'ssh-config'
import { encrypt, generatePassword, prisma } from '../common';
export const version = '3.8.2';
export const isDev = process.env.NODE_ENV === 'development';
export const includeServices: any = {
destinationDocker: true,
persistentStorage: true,
@@ -34,7 +18,8 @@ export const includeServices: any = {
moodle: true,
appwrite: true,
glitchTip: true,
searxng: true
searxng: true,
weblate: true
};
export async function configureServiceType({
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 {
await prisma.service.update({
where: { id },
@@ -338,7 +344,7 @@ export async function removeService({ id }: { id: string }): Promise<void> {
await prisma.moodle.deleteMany({ where: { serviceId: id } });
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
await prisma.searxng.deleteMany({ where: { serviceId: id } });
await prisma.weblate.deleteMany({ where: { serviceId: id } });
await prisma.service.delete({ where: { id } });
}

View File

@@ -63,6 +63,9 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
if (type === 'searxng') {
return await startSearXNGService(request)
}
if (type === 'weblate') {
return await startWeblateService(request)
}
throw `Service type ${type} not supported.`
} catch (error) {
throw { status: 500, message: error?.message || error }
@@ -2224,3 +2227,106 @@ async function startSearXNGService(request: FastifyRequest<ServiceStartStop>) {
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 })
}
}

View File

@@ -599,6 +599,54 @@ export const glitchTip = [{
isBoolean: false,
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',
isEditable: false,
@@ -624,7 +672,7 @@ export const glitchTip = [{
isEncrypted: true
},
{
name: 'defaultFromEmail',
name: 'defaultEmailFrom',
isEditable: true,
isLowerCase: false,
isNumber: false,
@@ -687,4 +735,53 @@ export const searxng = [{
isNumber: false,
isBoolean: false,
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
}]

View File

@@ -190,4 +190,15 @@ export const supportedServiceTypesAndVersions = [
main: 8080
}
},
{
name: 'weblate',
fancyName: 'Weblate',
baseImage: 'weblate/weblate',
images: ['postgres:14-alpine','redis:6-alpine'],
versions: ['latest'],
recommendedVersion: 'latest',
ports: {
main: 8080
}
},
];