add searxng

This commit is contained in:
Andras Bacsai 2022-10-20 09:18:13 +02:00
parent 21f3a70788
commit 22cbbec960
4 changed files with 157 additions and 19 deletions

View File

@ -7,6 +7,9 @@ export async function migrateServicesToNewTemplate() {
try {
const services = await prisma.service.findMany({ include: includeServices })
for (const service of services) {
if (!service.type) {
continue;
}
if (service.type === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service)
if (service.type === 'fider' && service.fider) await fider(service)
if (service.type === 'minio' && service.minio) await minio(service)
@ -16,7 +19,9 @@ 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)
if (service.type === 'glitchTip' && service.glitchTip) await glitchtip(service)
if (service.type === 'searxng' && service.searxng) await searxng(service)
await createVolumes(service);
}
} catch (error) {
@ -24,6 +29,23 @@ export async function migrateServicesToNewTemplate() {
}
}
async function searxng(service: any) {
const { secretKey, redisPassword } = service.searxng
const secrets = [
`SECRET_KEY@@@${secretKey}`,
`REDIS_PASSWORD@@@${redisPassword}`,
]
const settings = [
`SEARXNG_BASE_URL@@@$$generate_fqdn`
]
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 glitchtip(service: any) {
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, defaultEmail, defaultUsername, defaultPassword, defaultEmailFrom, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpUseTls, emailSmtpUseSsl, emailBackend, mailgunApiKey, sendgridApiKey, enableOpenUserRegistration } = service.glitchTip
@ -228,7 +250,7 @@ async function fider(service: any) {
}
async function plausibleAnalytics(service: any) {
const { email = 'admin@example.com', username = 'admin', password, postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, scriptName } = service.plausibleAnalytics;
const { email, username, password, postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, scriptName } = service.plausibleAnalytics;
const settings = [
`BASE_URL@@@$$generate_fqdn`,
@ -248,7 +270,6 @@ async function plausibleAnalytics(service: any) {
]
await migrateSettings(settings, service);
await migrateSecrets(secrets, service);
await createVolumes(service);
// Remove old service data
// await prisma.service.update({ where: { id: service.id }, data: { plausibleAnalytics: { delete: true } } })
@ -257,7 +278,10 @@ async function plausibleAnalytics(service: any) {
async function migrateSettings(settings: any[], service: any) {
for (const setting of settings) {
if (!setting) continue;
const [name, value] = setting.split('@@@')
let [name, value] = setting.split('@@@')
if (!value || value === 'null') {
continue;
}
// console.log('Migrating setting', name, value, 'for service', service.id, ', service name:', service.name)
await prisma.serviceSetting.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
@ -265,14 +289,17 @@ async function migrateSettings(settings: any[], service: any) {
async function migrateSecrets(secrets: any[], service: any) {
for (const secret of secrets) {
if (!secret) continue;
const [name, value] = secret.split('@@@')
let [name, value] = secret.split('@@@')
if (!value || value === 'null') {
continue
}
// console.log('Migrating secret', name, value, 'for service', service.id, ', service name:', service.name)
await prisma.serviceSecret.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSecret.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
}
async function createVolumes(service: any) {
const volumes = [];
let template = templates.find(t => t.name === service.type)
let template = templates.find(t => t.name === service.type.toLowerCase());
if (template) {
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id))
for (const s of Object.keys(template.services)) {

View File

@ -698,9 +698,15 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
const { workdir } = await createDirectories({ repository: type, buildId: id });
const template: any = await parseAndFindServiceTemplates(service, workdir, true)
const network = destinationDockerId && destinationDocker.network;
const config = {};
for (const service in template.services) {
let newEnviroments = []
for (const environment of template.services[service].environment) {
const [env, value] = environment.split("=");
if (!value.startsWith('$$secret') && value !== '') {
newEnviroments.push(`${env}=${value}`)
}
}
config[service] = {
container_name: service,
build: template.services[service].build || undefined,
@ -709,9 +715,11 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
expose: template.services[service].ports,
// ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
volumes: template.services[service].volumes,
environment: template.services[service].environment,
environment: newEnviroments,
depends_on: template.services[service].depends_on,
ulimits: template.services[service].ulimits,
cap_drop: template.services[service].cap_drop,
cap_add: template.services[service].cap_add,
labels: makeLabelForServices(type),
...defaultComposeConfiguration(network),
}

View File

@ -1,4 +1,87 @@
export default [
{
"templateVersion": "1.0.0",
"serviceDefaultVersion": "2022.10.14-1a5b0965",
"name": "searxng",
"displayName": "SearXNG",
"description": "",
"services": {
"$$id": {
"name": "SearXNG",
"depends_on": [
"$$id-redis"
],
"image": "searxng/searxng:$$core_version",
"volumes": [
"$$id-postgresql-searxng:/etc/searxng",
],
"environment": [
"SEARXNG_BASE_URL=$$config_searxng_base_url",
"SECRET_KEY=$$secret_secret_key",
],
"ports": [
"8080"
],
"extras": {
"files": [
{
source: "$$workdir/schema.postgresql.sql",
destination: "/docker-entrypoint-initdb.d/schema.postgresql.sql",
content: `
# see https://docs.searxng.org/admin/engines/settings.html#use-default-settings
use_default_settings: true
server:
secret_key: $$secret_secret_key
limiter: true
image_proxy: true
ui:
static_use_hash: true
redis:
url: redis://:$$secret_redis_password@$$id-redis:6379/0`
}
]
}
},
"$$id-redis": {
"name": "Redis",
"command": `redis-server --requirepass $$secret_redis_password --save "" --appendonly "no"`,
"depends_on": [],
"image": "redis:7-alpine",
"volumes": [
"$$id-redis-data:/data",
],
"environment": [
"REDIS_PASSWORD=$$secret_redis_password",
],
"ports": [],
"cap_drop": ['ALL'],
"cap_add": ['SETGID', 'SETUID', 'DAC_OVERRIDE'],
}
},
"variables": [
{
"id": "$$config_searxng_base_url",
"name": "SEARXNG_BASE_URL",
"label": "SearXNG Base URL",
"defaultValue": "$$generate_fqdn",
"description": "",
},
{
"id": "$$secret_secret_key",
"name": "SECRET_KEY",
"label": "Secret Key",
"defaultValue": "$$generate_passphrase",
"description": "",
},
{
"id": "$$secret_redis_password",
"name": "REDIS_PASSWORD",
"label": "Redis Password",
"defaultValue": "$$generate_password",
"description": "",
}
]
},
{
"templateVersion": "1.0.0",
"serviceDefaultVersion": "v2.0.6",
@ -9,7 +92,8 @@ export default [
"$$id": {
"name": "GlitchTip",
"depends_on": [
"$$id-postgresql"
"$$id-postgresql",
"$$id-redis"
],
"image": "glitchtip/glitchtip:$$core_version",
"volumes": [],
@ -67,7 +151,7 @@ export default [
{
"id": "$$config_glitchtip_domain",
"name": "GLITCHTIP_DOMAIN",
"label": "GLITCHTIP_DOMAIN URL",
"label": "GlitchTip Domain",
"defaultValue": "$$generate_fqdn",
"description": "",
},

View File

@ -6,13 +6,13 @@ import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage
import { day } from '../../../../lib/dayjs';
import { checkContainer, isContainerExited } from '../../../../lib/docker';
import cuid from 'cuid';
import templates from '../../../../lib/templates';
import type { OnlyId } from '../../../../types';
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetGlitchTipSettings, SetWordpressSettings } from './types';
import { supportedServiceTypesAndVersions } from '../../../../lib/services/supportedVersions';
import { configureServiceType, removeService } from '../../../../lib/services/common';
import { hashPassword } from '../handlers';
import templates from '../../../../lib/templates';
export async function listServices(request: FastifyRequest) {
try {
@ -113,7 +113,7 @@ export async function getServiceStatus(request: FastifyRequest<OnlyId>) {
}
}
export async function parseAndFindServiceTemplates(service: any, workdir?: string, isDeploy: boolean = false) {
const foundTemplate = templates.find(t => t.name === service.type)
const foundTemplate = templates.find(t => t.name === service.type.toLowerCase())
let parsedTemplate = {}
if (foundTemplate) {
if (!isDeploy) {
@ -155,12 +155,13 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
if (service.serviceSetting.length > 0) {
for (const setting of service.serviceSetting) {
const { name, value } = setting
const regex = new RegExp(`\\$\\$config_${name}\\"`, 'gi')
if (service.fqdn && value === '$$generate_fqdn') {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, service.fqdn))
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(regex, service.fqdn + "\""))
} else if (service.fqdn && value === '$$generate_domain') {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, getDomain(service.fqdn)))
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(regex, getDomain(service.fqdn) + "\""))
} else {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, value))
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(regex, value + "\""))
}
}
@ -170,7 +171,9 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
if (service.serviceSecret.length > 0) {
for (const secret of service.serviceSecret) {
const { name, value } = secret
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$hashed$$secret_${name.toLowerCase()}`, bcrypt.hashSync(value, 10)).replaceAll(`$$secret_${name.toLowerCase()}`, value))
const regex = new RegExp(`\\$\\$secret_${name}\\"`, 'gi')
const regexHashed = new RegExp(`\\$\\$hashed\\$\\$secret_${name}\\"`, 'gi')
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(regexHashed, bcrypt.hashSync(value, 10)).replaceAll(regex, value))
}
}
}
@ -185,13 +188,17 @@ export async function getService(request: FastifyRequest<OnlyId>) {
if (!service) {
throw { status: 404, message: 'Service not found.' }
}
const template = await parseAndFindServiceTemplates(service)
let template = {}
if (service.type) {
template = await parseAndFindServiceTemplates(service)
}
return {
settings: await listSettings(),
service,
template,
}
} catch ({ status, message }) {
console.log(status, message)
return errorHandler({ status, message })
}
}
@ -218,19 +225,22 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
if (foundTemplate.variables.length > 0) {
foundTemplate.variables = foundTemplate.variables.map(variable => {
let { id: variableId } = variable;
console.log(variableId)
if (variableId.startsWith('$$secret_')) {
const length = variable?.extras && variable.extras['length']
if (variable.defaultValue === '$$generate_password') {
variable.value = generatePassword({ length });
} else if (variable.defaultValue === '$$generate_passphrase') {
variable.value = generatePassword({ length });
} else if (!variable.defaultValue) {
variable.defaultValue = undefined
}
}
if (variableId.startsWith('$$config_')) {
if (variable.defaultValue === '$$generate_username') {
variable.value = cuid();
} else {
variable.value = variable.defaultValue
variable.value = variable.defaultValue || ''
}
}
if (variable.value) {
@ -246,19 +256,28 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
variable.value = variable.defaultValue
for (const generatedVariable of generatedVariables) {
let [id, value] = generatedVariable.split('=')
variable.value = variable.value.replaceAll(id, value)
if (variable.value) {
variable.value = variable.value.replaceAll(id, value)
}
}
}
return variable
})
}
for (const variable of foundTemplate.variables) {
if (variable.id.startsWith('$$secret_')) {
if (!variable.value) {
continue;
}
await prisma.serviceSecret.create({
data: { name: variable.name, value: encrypt(variable.value), service: { connect: { id } } }
})
}
if (variable.id.startsWith('$$config_')) {
if (!variable.value) {
variable.value = '';
}
await prisma.serviceSetting.create({
data: { name: variable.name, value: variable.value, service: { connect: { id } } }
})