work-work
This commit is contained in:
parent
a7e86d9afd
commit
8f660c0276
@ -0,0 +1,13 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServiceSetting" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name");
|
@ -398,6 +398,7 @@ model Service {
|
||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||
persistentStorage ServicePersistentStorage[]
|
||||
serviceSecret ServiceSecret[]
|
||||
serviceSetting ServiceSetting[]
|
||||
teams Team[]
|
||||
|
||||
fider Fider?
|
||||
@ -417,6 +418,18 @@ model Service {
|
||||
taiga Taiga?
|
||||
}
|
||||
|
||||
model ServiceSetting {
|
||||
id String @id @default(cuid())
|
||||
serviceId String
|
||||
name String
|
||||
value String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
|
||||
@@unique([serviceId, name])
|
||||
}
|
||||
|
||||
model PlausibleAnalytics {
|
||||
id String @id @default(cuid())
|
||||
email String?
|
||||
|
@ -1454,7 +1454,6 @@ export async function getServiceFromDB({
|
||||
}
|
||||
let { type } = body;
|
||||
type = fixType(type);
|
||||
|
||||
if (body?.serviceSecret.length > 0) {
|
||||
body.serviceSecret = body.serviceSecret.map((s) => {
|
||||
s.value = decrypt(s.value);
|
||||
@ -1462,7 +1461,7 @@ export async function getServiceFromDB({
|
||||
});
|
||||
}
|
||||
|
||||
body[type] = { ...body[type], ...getUpdateableFields(type, body[type]) };
|
||||
// body[type] = { ...body[type], ...getUpdateableFields(type, body[type]) };
|
||||
return { ...body, settings };
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ export const includeServices: any = {
|
||||
destinationDocker: true,
|
||||
persistentStorage: true,
|
||||
serviceSecret: true,
|
||||
serviceSetting: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
@ -362,6 +363,7 @@ export async function configureServiceType({
|
||||
|
||||
export async function removeService({ id }: { id: string }): Promise<void> {
|
||||
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.serviceSetting.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||
|
@ -7,6 +7,7 @@ import { asyncSleep, ComposeFile, createDirectories, defaultComposeConfiguration
|
||||
import { defaultServiceConfigurations } from '../services';
|
||||
import { OnlyId } from '../../types';
|
||||
import templates from '../templates'
|
||||
import { parseAndFindServiceTemplates } from '../../routes/api/v1/services/handlers';
|
||||
|
||||
// export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
// try {
|
||||
@ -691,36 +692,36 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
const teamId = request.user.teamId;
|
||||
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort, persistentStorage } =
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret,serviceSetting, exposePort, persistentStorage } =
|
||||
service;
|
||||
|
||||
let template = templates.find((template) => template.name === type);
|
||||
|
||||
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', id).replaceAll('$$fqdn', service.fqdn))
|
||||
|
||||
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) {
|
||||
console.log(template.services[service])
|
||||
config[service] = {
|
||||
container_name: id,
|
||||
image: template.services[service].image.replace('$$core_version', version),
|
||||
container_name: service,
|
||||
image: template.services[service].image,
|
||||
expose: template.services[service].ports,
|
||||
// ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
volumes: template.services[service].volumes,
|
||||
environment: {},
|
||||
environment: template.services[service].environment,
|
||||
depends_on: template.services[service].depends_on,
|
||||
ulimits: template.services[service].ulimits,
|
||||
labels: makeLabelForServices(type),
|
||||
...defaultComposeConfiguration(network),
|
||||
}
|
||||
if (serviceSecret.length > 0) {
|
||||
serviceSecret.forEach((secret) => {
|
||||
config[service].environment[secret.name] = secret.value;
|
||||
});
|
||||
|
||||
// Generate files for builds
|
||||
if (template.services[service].build) {
|
||||
if (template.services[service]?.extras?.files?.length > 0) {
|
||||
console.log(template.services[service]?.extras?.files)
|
||||
}
|
||||
}
|
||||
}
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||
|
||||
const composeFile: ComposeFile = {
|
||||
|
@ -4,10 +4,10 @@ export default [
|
||||
"serviceDefaultVersion": "0.198.1",
|
||||
"name": "n8n",
|
||||
"displayName": "n8n.io",
|
||||
"isOfficial": true,
|
||||
"description": "n8n is a free and open node based Workflow Automation Tool.",
|
||||
"services": {
|
||||
"$$id": {
|
||||
"name": "N8n",
|
||||
"documentation": "Taken from https://hub.docker.com/r/n8nio/n8n",
|
||||
"depends_on": [],
|
||||
"image": "n8nio/n8n:$$core_version",
|
||||
@ -17,24 +17,31 @@ export default [
|
||||
"/var/run/docker.sock:/var/run/docker.sock"
|
||||
],
|
||||
"environment": [
|
||||
"WEBHOOK_URL=$$fqdn"
|
||||
"WEBHOOK_URL=$$config_webhook_url"
|
||||
],
|
||||
"ports": [
|
||||
"5678"
|
||||
]
|
||||
}
|
||||
},
|
||||
"variables": []
|
||||
"variables": [
|
||||
{
|
||||
"id": "$$config_webhook_url",
|
||||
"name": "WEBHOOK_URL",
|
||||
"label": "Webhook URL",
|
||||
"defaultValue": "$$generate_fqdn",
|
||||
"description": "",
|
||||
}]
|
||||
},
|
||||
{
|
||||
"templateVersion": "1.0.0",
|
||||
"serviceDefaultVersion": "stable",
|
||||
"name": "plausibleanalytics",
|
||||
"displayName": "PlausibleAnalytics",
|
||||
"isOfficial": true,
|
||||
"description": "Plausible is a lightweight and open-source website analytics tool.",
|
||||
"services": {
|
||||
"$$id": {
|
||||
"name": "Plausible Analytics",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"command": ['sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"'],
|
||||
"depends_on": [
|
||||
@ -43,31 +50,33 @@ export default [
|
||||
],
|
||||
"image": "plausible/analytics:$$core_version",
|
||||
"environment": [
|
||||
"ADMIN_USER_EMAIL=$$secret_email",
|
||||
"ADMIN_USER_NAME=$$secret_name",
|
||||
"ADMIN_USER_PASSWORD=$$secret_password",
|
||||
"BASE_URL=$$fqdn",
|
||||
"SECRET_KEY_BASE=$$secret_key_base",
|
||||
"DISABLE_AUTH=$$secret_disable_auth",
|
||||
"DISABLE_REGISTRATION=$$secret_disable_registration",
|
||||
"DATABASE_URL=postgresql://$$secret_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$secret_postgresql_database",
|
||||
"CLICKHOUSE_DATABASE_URL=http://$$id-clickhouse:8123/plausible",
|
||||
"ADMIN_USER_EMAIL=$$config_admin_user_email",
|
||||
"ADMIN_USER_NAME=$$config_admin_user_name",
|
||||
"ADMIN_USER_PASSWORD=$$secret_admin_user_password",
|
||||
"BASE_URL=$$config_base_url",
|
||||
"SECRET_KEY_BASE=$$secret_secret_key_base",
|
||||
"DISABLE_AUTH=$$config_disable_auth",
|
||||
"DISABLE_REGISTRATION=$$config_disable_registration",
|
||||
"DATABASE_URL=$$secret_database_url",
|
||||
"CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url",
|
||||
],
|
||||
"ports": [
|
||||
"8000"
|
||||
],
|
||||
},
|
||||
"$$id-postgresql": {
|
||||
"name": "PostgreSQL",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"image": "bitnami/postgresql:13.2.0",
|
||||
"environment": [
|
||||
"POSTGRESQL_PASSWORD=$$secret_postgresql_password",
|
||||
"POSTGRESQL_USERNAME=$$secret_postgresql_username",
|
||||
"POSTGRESQL_DATABASE=$$secret_postgresql_database",
|
||||
"POSTGRESQL_USERNAME=$$config_postgresql_username",
|
||||
"POSTGRESQL_DATABASE=$$config_postgresql_database",
|
||||
],
|
||||
|
||||
},
|
||||
"$$id-clickhouse": {
|
||||
"name": "Clickhouse",
|
||||
"documentation": "Taken from https://plausible.io/",
|
||||
"build": "$$workdir",
|
||||
"image": "yandex/clickhouse-server:21.3.2.5",
|
||||
@ -102,69 +111,98 @@ export default [
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"id": "$$secret_email",
|
||||
"label": "Admin Email",
|
||||
"id": "$$config_base_url",
|
||||
"name": "BASE_URL",
|
||||
"label": "Base URL",
|
||||
"defaultValue": "$$generate_fqdn",
|
||||
"description": "You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance.",
|
||||
},
|
||||
{
|
||||
"id": "$$secret_database_url",
|
||||
"name": "DATABASE_URL",
|
||||
"label": "Database URL for PostgreSQL",
|
||||
"defaultValue": "postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database",
|
||||
"description": "",
|
||||
},
|
||||
{
|
||||
"id": "$$secret_clickhouse_database_url",
|
||||
"name": "CLICKHOUSE_DATABASE_URL",
|
||||
"label": "Database URL for Clickhouse",
|
||||
"defaultValue": "http://$$id-clickhouse:8123/plausible",
|
||||
"description": "",
|
||||
},
|
||||
{
|
||||
"id": "$$config_admin_user_email",
|
||||
"name": "ADMIN_USER_EMAIL",
|
||||
"label": "Admin Email Address",
|
||||
"defaultValue": "admin@example.com",
|
||||
"description": "This is the admin email. Please change it.",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_name",
|
||||
"label": "Admin Name",
|
||||
"id": "$$config_admin_user_name",
|
||||
"name": "ADMIN_USER_NAME",
|
||||
"label": "Admin User Name",
|
||||
"defaultValue": "$$generate_username",
|
||||
"description": "This is the admin username. Please change it.",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_password",
|
||||
"label": "Admin Password",
|
||||
"defaultValue":"$$generate_password",
|
||||
"id": "$$secret_admin_user_password",
|
||||
"name": "ADMIN_USER_PASSWORD",
|
||||
"showAsConfiguration": true,
|
||||
"label": "Admin User Password",
|
||||
"defaultValue": "$$generate_password",
|
||||
"description": "This is the admin password. Please change it.",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_secret_key_base",
|
||||
"name": "SECRET_KEY_BASE",
|
||||
"label": "Secret Key Base",
|
||||
"defaultValue":"$$generate_passphrase",
|
||||
"defaultValue": "$$generate_passphrase",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_disable_auth",
|
||||
"label": "Disable Auth",
|
||||
"id": "$$config_disable_auth",
|
||||
"name": "DISABLE_AUTH",
|
||||
"label": "Disable Authentication",
|
||||
"defaultValue": "false",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_disable_registration",
|
||||
"id": "$$config_disable_registration",
|
||||
"name": "DISABLE_REGISTRATION",
|
||||
"label": "Disable Registration",
|
||||
"defaultValue": "true",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_postgresql_username",
|
||||
"id": "$$config_postgresql_username",
|
||||
"name": "POSTGRESQL_USERNAME",
|
||||
"label": "PostgreSQL Username",
|
||||
"defaultValue": "postgresql",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
},
|
||||
{
|
||||
"id": "$$secret_postgresql_password",
|
||||
"name": "POSTGRESQL_PASSWORD",
|
||||
"label": "PostgreSQL Password",
|
||||
"defaultValue": "$$generate_password",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
}
|
||||
,
|
||||
{
|
||||
"id": "$$secret_postgresql_database",
|
||||
"id": "$$config_postgresql_database",
|
||||
"name": "POSTGRESQL_DATABASE",
|
||||
"label": "PostgreSQL Database",
|
||||
"defaultValue": "plausible",
|
||||
"description": "",
|
||||
"validRegex": /^([^\s^\/])+$/
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "$$config_scriptName",
|
||||
"name": "SCRIPT_NAME",
|
||||
"label": "Custom Script Name",
|
||||
"defaultValue": "plausible.js",
|
||||
"description": "This is the default script name.",
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -114,13 +114,70 @@ export async function getServiceStatus(request: FastifyRequest<OnlyId>) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
function parseAndFindServiceTemplates(service: any) {
|
||||
export async function parseAndFindServiceTemplates(service: any, workdir?: string, isDeploy: boolean = false) {
|
||||
const foundTemplate = templates.find(t => t.name === service.type)
|
||||
let parsedTemplate = {}
|
||||
if (foundTemplate) {
|
||||
return JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', service.id).replaceAll('$$fqdn', service.fqdn))
|
||||
if (!isDeploy) {
|
||||
for (const [key, value] of Object.entries(foundTemplate.services)) {
|
||||
const realKey = key.replace('$$id', service.id)
|
||||
parsedTemplate[realKey] = {
|
||||
name: value.name,
|
||||
image: value.image,
|
||||
environment: []
|
||||
}
|
||||
if (value.environment?.length > 0) {
|
||||
for (const env of value.environment) {
|
||||
const [envKey, envValue] = env.split('=')
|
||||
const label = foundTemplate.variables.find(v => v.name === envKey)?.label
|
||||
const description = foundTemplate.variables.find(v => v.name === envKey)?.description
|
||||
const defaultValue = foundTemplate.variables.find(v => v.name === envKey)?.defaultValue
|
||||
const showAsConfiguration = foundTemplate.variables.find(v => v.name === envKey)?.showAsConfiguration
|
||||
if (envValue.startsWith('$$config') || showAsConfiguration) {
|
||||
parsedTemplate[realKey].environment.push(
|
||||
{ name: envKey, value: envValue, label, description, defaultValue }
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parsedTemplate = foundTemplate
|
||||
}
|
||||
// replace $$id and $$workdir
|
||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll('$$id', service.id).replaceAll('$$core_version', foundTemplate.serviceDefaultVersion))
|
||||
|
||||
// replace $$fqdn
|
||||
if (workdir) {
|
||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll('$$workdir', workdir))
|
||||
}
|
||||
|
||||
// replace $$config
|
||||
if (service.serviceSetting.length > 0) {
|
||||
for (const setting of service.serviceSetting) {
|
||||
const { name, value } = setting
|
||||
if (service.fqdn && value === '$$generate_fqdn') {
|
||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, service.fqdn))
|
||||
} else {
|
||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, value))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replace $$secret
|
||||
if (service.serviceSecret.length > 0) {
|
||||
for (const secret of service.serviceSecret) {
|
||||
const { name, value } = secret
|
||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$secret_${name.toLowerCase()}`, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsedTemplate
|
||||
|
||||
}
|
||||
|
||||
export async function getService(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
@ -129,10 +186,11 @@ export async function getService(request: FastifyRequest<OnlyId>) {
|
||||
if (!service) {
|
||||
throw { status: 404, message: 'Service not found.' }
|
||||
}
|
||||
const template = await parseAndFindServiceTemplates(service)
|
||||
return {
|
||||
settings: await listSettings(),
|
||||
service,
|
||||
template: parseAndFindServiceTemplates(service)
|
||||
template,
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
@ -150,30 +208,69 @@ export async function getServiceType(request: FastifyRequest) {
|
||||
export async function saveServiceType(request: FastifyRequest<SaveServiceType>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { name, variables = [], serviceDefaultVersion = 'latest' } = request.body;
|
||||
if (variables.length > 0) {
|
||||
for (const variable of variables) {
|
||||
const { id: variableId, defaultValue, value = null } = variable;
|
||||
if (variableId.startsWith('$$secret_')) {
|
||||
const secretName = variableId.replace('$$secret_', '');
|
||||
let secretValue = defaultValue || value || null;
|
||||
if (defaultValue === '$$generate_password') {
|
||||
secretValue = generatePassword({});
|
||||
const { type } = request.body;
|
||||
let foundTemplate = templates.find(t => t.name === type)
|
||||
if (foundTemplate) {
|
||||
let generatedVariables = new Set()
|
||||
let missingVariables = new Set()
|
||||
|
||||
foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id))
|
||||
|
||||
if (foundTemplate.variables.length > 0) {
|
||||
foundTemplate.variables = foundTemplate.variables.map(variable => {
|
||||
let { id: variableId } = variable;
|
||||
if (variableId.startsWith('$$secret_')) {
|
||||
if (variable.defaultValue === '$$generate_password') {
|
||||
variable.value = generatePassword({});
|
||||
} else if (variable.defaultValue === '$$generate_passphrase') {
|
||||
variable.value = cuid();
|
||||
}
|
||||
}
|
||||
if (defaultValue === '$$generate_username') {
|
||||
secretValue = cuid();
|
||||
if (variableId.startsWith('$$config_')) {
|
||||
if (variable.defaultValue === '$$generate_username') {
|
||||
variable.value = cuid();
|
||||
} else {
|
||||
variable.value = variable.defaultValue
|
||||
}
|
||||
}
|
||||
if (defaultValue === '$$generate_passphrase') {
|
||||
secretValue = cuid();
|
||||
if (variable.value) {
|
||||
generatedVariables.add(`${variableId}=${variable.value}`)
|
||||
} else {
|
||||
missingVariables.add(variableId)
|
||||
}
|
||||
await prisma.serviceSecret.create({
|
||||
data: { name: secretName, value: encrypt(secretValue), service: { connect: { id } } }
|
||||
return variable
|
||||
})
|
||||
if (missingVariables.size > 0) {
|
||||
foundTemplate.variables = foundTemplate.variables.map(variable => {
|
||||
if (missingVariables.has(variable.id)) {
|
||||
variable.value = variable.defaultValue
|
||||
for (const generatedVariable of generatedVariables) {
|
||||
let [id, value] = generatedVariable.split('=')
|
||||
variable.value = variable.value.replaceAll(id, value)
|
||||
}
|
||||
}
|
||||
return variable
|
||||
})
|
||||
}
|
||||
for (const variable of foundTemplate.variables) {
|
||||
if (variable.id.startsWith('$$secret_')) {
|
||||
await prisma.serviceSecret.create({
|
||||
data: { name: variable.name, value: encrypt(variable.value), service: { connect: { id } } }
|
||||
})
|
||||
}
|
||||
if (variable.id.startsWith('$$config_')) {
|
||||
await prisma.serviceSetting.create({
|
||||
data: { name: variable.name, value: variable.value, service: { connect: { id } } }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
await prisma.service.update({ where: { id }, data: { type, version: foundTemplate.serviceDefaultVersion } })
|
||||
return reply.code(201).send()
|
||||
} else {
|
||||
throw { status: 404, message: 'Service type not found.' }
|
||||
}
|
||||
await prisma.service.update({ where: { id }, data: { type: name, version: serviceDefaultVersion } })
|
||||
return reply.code(201).send()
|
||||
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
@ -344,23 +441,30 @@ export async function checkService(request: FastifyRequest<CheckService>) {
|
||||
export async function saveService(request: FastifyRequest<SaveService>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
let { name, fqdn, exposePort, type } = request.body;
|
||||
|
||||
let { name, fqdn, exposePort, type, serviceSetting } = request.body;
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
type = fixType(type)
|
||||
const update = saveUpdateableFields(type, request.body[type])
|
||||
// const update = saveUpdateableFields(type, request.body[type])
|
||||
const data = {
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
}
|
||||
if (Object.keys(update).length > 0) {
|
||||
data[type] = { update: update }
|
||||
// if (Object.keys(update).length > 0) {
|
||||
// data[type] = { update: update }
|
||||
// }
|
||||
for (const setting of serviceSetting) {
|
||||
const { id: settingId, value, changed = false } = setting
|
||||
if (setting.changed) {
|
||||
await prisma.serviceSetting.update({ where: { id: settingId }, data: { value } })
|
||||
|
||||
}
|
||||
}
|
||||
await prisma.service.update({
|
||||
where: { id }, data
|
||||
|
||||
});
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
|
@ -72,6 +72,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get<OnlyId>('/:id/usage', async (request) => await getServiceUsage(request));
|
||||
fastify.get<GetServiceLogs>('/:id/logs', async (request) => await getServiceLogs(request));
|
||||
|
||||
fastify.post<ServiceStartStop>('/:id/start', async (request) => await startService(request));
|
||||
fastify.post<ServiceStartStop>('/:id/:type/start', async (request) => await startService(request));
|
||||
fastify.post<ServiceStartStop>('/:id/:type/stop', async (request) => await stopService(request));
|
||||
fastify.post<ServiceStartStop & SetWordpressSettings & SetGlitchTipSettings>('/:id/:type/settings', async (request, reply) => await setSettingsService(request, reply));
|
||||
|
@ -23,7 +23,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row border-b border-coolgray-500 my-6 space-x-2">
|
||||
<!-- <div class="flex flex-row border-b border-coolgray-500 my-6 space-x-2">
|
||||
<div class="title font-bold pb-3">Plausible Analytics</div>
|
||||
<ServiceStatus id={service.id} />
|
||||
</div>
|
||||
@ -123,4 +123,4 @@
|
||||
<div class="flex flex-row my-6 space-x-2">
|
||||
<div class="title font-bold pb-3">ClickHouse</div>
|
||||
<ServiceStatus id={`${service.id}-clickhouse`} />
|
||||
</div>
|
||||
</div> -->
|
@ -41,6 +41,7 @@
|
||||
},
|
||||
stuff: {
|
||||
service,
|
||||
template,
|
||||
readOnly,
|
||||
settings
|
||||
}
|
||||
@ -111,7 +112,7 @@
|
||||
$status.service.initialLoading = true;
|
||||
$status.service.loading = true;
|
||||
try {
|
||||
await post(`/services/${service.id}/${service.type}/start`, {});
|
||||
await post(`/services/${service.id}/start`, {});
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export let services: any;
|
||||
|
||||
|
||||
let search = '';
|
||||
let filteredServices = services;
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
async function handleSubmit(service: any) {
|
||||
try {
|
||||
await post(`/services/${id}/configuration/type`, { ...service });
|
||||
await post(`/services/${id}/configuration/type`, { type: service.name });
|
||||
return await goto(from || `/services/${id}`);
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
|
@ -10,11 +10,9 @@
|
||||
<script lang="ts">
|
||||
export let service: any;
|
||||
export let readOnly: any;
|
||||
export let settings: any;
|
||||
|
||||
export let template: any;
|
||||
import cuid from 'cuid';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { browser } from '$app/env';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
@ -31,16 +29,18 @@
|
||||
} from '$lib/store';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import * as Services from '$lib/components/Services';
|
||||
// import * as Services from '$lib/components/Services';
|
||||
|
||||
import DocLink from '$lib/components/DocLink.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import ServiceStatus from '$lib/components/ServiceStatus.svelte';
|
||||
|
||||
const { id } = $page.params;
|
||||
let serviceName: any = service.type && service.type[0].toUpperCase() + service.type.substring(1);
|
||||
// let serviceName: any = service.type && service.type[0].toUpperCase() + service.type.substring(1);
|
||||
$: isDisabled =
|
||||
!$appSession.isAdmin || $status.service.isRunning || $status.service.initialLoading;
|
||||
|
||||
let newConfiguration = null;
|
||||
let forceSave = false;
|
||||
let loading = {
|
||||
save: false,
|
||||
@ -69,17 +69,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
async function handleSubmit(e: any) {
|
||||
const formData = new FormData(e.target);
|
||||
for (let field of formData) {
|
||||
const [key, value] = field;
|
||||
for (const setting of service.serviceSetting) {
|
||||
if (setting.name === key && setting.value !== value) {
|
||||
setting.changed = true;
|
||||
setting.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loading.save) return;
|
||||
loading.save = true;
|
||||
try {
|
||||
await post(`/services/${id}/check`, {
|
||||
fqdn: service.fqdn,
|
||||
forceSave,
|
||||
dualCerts,
|
||||
otherFqdns: service.minio?.apiFqdn ? [service.minio?.apiFqdn] : [],
|
||||
exposePort: service.exposePort
|
||||
});
|
||||
// await post(`/services/${id}/check`, {
|
||||
// fqdn: service.fqdn,
|
||||
// forceSave,
|
||||
// dualCerts,
|
||||
// otherFqdns: service.minio?.apiFqdn ? [service.minio?.apiFqdn] : [],
|
||||
// exposePort: service.exposePort
|
||||
// });
|
||||
await post(`/services/${id}`, { ...service });
|
||||
setLocation(service);
|
||||
forceSave = false;
|
||||
@ -174,10 +184,10 @@
|
||||
if (service.type === 'wordpress') {
|
||||
service.wordpress.mysqlDatabase = 'db';
|
||||
}
|
||||
if (service.type === 'plausibleanalytics') {
|
||||
service.plausibleAnalytics.email = 'noreply@demo.com';
|
||||
service.plausibleAnalytics.username = 'admin';
|
||||
}
|
||||
// if (service.type === 'plausibleanalytics') {
|
||||
// service.plausibleAnalytics.email = 'noreply@demo.com';
|
||||
// service.plausibleAnalytics.username = 'admin';
|
||||
// }
|
||||
if (service.type === 'minio') {
|
||||
service.minio.apiFqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
@ -187,13 +197,13 @@
|
||||
if (service.type === 'fider') {
|
||||
service.fider.emailNoreply = 'noreply@demo.com';
|
||||
}
|
||||
await handleSubmit();
|
||||
// await handleSubmit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<form on:submit|preventDefault={() => handleSubmit()}>
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<div class="mx-auto w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
|
||||
<div class="title font-bold pb-3 ">General</div>
|
||||
@ -213,7 +223,7 @@
|
||||
: $t('forms.save')}</button
|
||||
>
|
||||
{/if}
|
||||
{#if service.type === 'plausibleanalytics' && $status.service.isRunning}
|
||||
<!-- {#if service.type === 'plausibleanalytics' && $status.service.isRunning}
|
||||
<div class="btn-group">
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
@ -231,7 +241,7 @@
|
||||
class:loading={loading.cleanup}>Cleanup Unnecessary Database Logs</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{/if} -->
|
||||
{#if service.type === 'appwrite' && $status.service.isRunning}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
@ -412,6 +422,41 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:component this={Services[serviceName]} bind:service {readOnly} {settings} />
|
||||
<div />
|
||||
<div>
|
||||
{#each Object.keys(template) as oneService}
|
||||
<div class="flex flex-row border-b border-coolgray-500 my-6 space-x-2">
|
||||
<div class="title font-bold pb-3">{template[oneService].name}</div>
|
||||
<ServiceStatus id={template[oneService]} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
{#if template[oneService].environment.length > 0}
|
||||
{#each template[oneService].environment as variable}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for={variable.name}>{variable.label}</label>
|
||||
{#if variable.defaultValue === '$$generate_fqdn'}
|
||||
<input
|
||||
class="w-full"
|
||||
disabled
|
||||
readonly
|
||||
name={variable.name}
|
||||
id={variable.name}
|
||||
value={service.fqdn}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
class="w-full"
|
||||
name={variable.name}
|
||||
id={variable.name}
|
||||
value={variable.value}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user