Features: - Integrated the first service: [Plausible Analytics](https://plausible.io)! Fixes: - UI/UX fixes and new designs
194 lines
6.0 KiB
JavaScript
194 lines
6.0 KiB
JavaScript
const yaml = require('js-yaml')
|
|
const fs = require('fs').promises
|
|
const cuid = require('cuid')
|
|
const { docker } = require('../../../libs/docker')
|
|
const { execShellAsync } = require('../../../libs/common')
|
|
const { saveServerLog } = require('../../../libs/logging')
|
|
|
|
const { uniqueNamesGenerator, adjectives, colors, animals } = require('unique-names-generator')
|
|
const generator = require('generate-password')
|
|
|
|
function getUniq () {
|
|
return uniqueNamesGenerator({ dictionaries: [adjectives, animals, colors], length: 2 })
|
|
}
|
|
module.exports = async function (fastify) {
|
|
fastify.get('/:deployId', async (request, reply) => {
|
|
const { deployId } = request.params
|
|
try {
|
|
const database = (await docker.engine.listServices()).find(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'database' && JSON.parse(r.Spec.Labels.configuration).general.deployId === deployId)
|
|
if (database) {
|
|
const jsonEnvs = {}
|
|
if (database.Spec.TaskTemplate.ContainerSpec.Env) {
|
|
for (const d of database.Spec.TaskTemplate.ContainerSpec.Env) {
|
|
const s = d.split('=')
|
|
jsonEnvs[s[0]] = s[1]
|
|
}
|
|
}
|
|
const payload = {
|
|
config: JSON.parse(database.Spec.Labels.configuration),
|
|
envs: jsonEnvs || null
|
|
}
|
|
reply.code(200).send(payload)
|
|
} else {
|
|
throw new Error()
|
|
}
|
|
} catch (error) {
|
|
throw new Error('No database found?')
|
|
}
|
|
})
|
|
|
|
const postSchema = {
|
|
body: {
|
|
type: 'object',
|
|
properties: {
|
|
type: { type: 'string', enum: ['mongodb', 'postgresql', 'mysql', 'couchdb', 'clickhouse'] }
|
|
},
|
|
required: ['type']
|
|
}
|
|
}
|
|
|
|
fastify.post('/deploy', { schema: postSchema }, async (request, reply) => {
|
|
try {
|
|
let { type, defaultDatabaseName } = request.body
|
|
const passwords = generator.generateMultiple(2, {
|
|
length: 24,
|
|
numbers: true,
|
|
strict: true
|
|
})
|
|
const usernames = generator.generateMultiple(2, {
|
|
length: 10,
|
|
numbers: true,
|
|
strict: true
|
|
})
|
|
// TODO: Query for existing db with the same name
|
|
const nickname = getUniq()
|
|
|
|
if (!defaultDatabaseName) defaultDatabaseName = nickname
|
|
|
|
reply.code(201).send({ message: 'Deploying.' })
|
|
// TODO: Persistent volume, custom inputs
|
|
const deployId = cuid()
|
|
const configuration = {
|
|
general: {
|
|
workdir: `/tmp/${deployId}`,
|
|
deployId,
|
|
nickname,
|
|
type
|
|
},
|
|
database: {
|
|
usernames,
|
|
passwords,
|
|
defaultDatabaseName
|
|
},
|
|
deploy: {
|
|
name: nickname
|
|
}
|
|
}
|
|
await execShellAsync(`mkdir -p ${configuration.general.workdir}`)
|
|
let generateEnvs = {}
|
|
let image = null
|
|
let volume = null
|
|
let ulimits = {}
|
|
if (type === 'mongodb') {
|
|
generateEnvs = {
|
|
MONGODB_ROOT_PASSWORD: passwords[0],
|
|
MONGODB_USERNAME: usernames[0],
|
|
MONGODB_PASSWORD: passwords[1],
|
|
MONGODB_DATABASE: defaultDatabaseName
|
|
}
|
|
image = 'bitnami/mongodb:4.4'
|
|
volume = `${configuration.general.deployId}-${type}-data:/bitnami/mongodb`
|
|
} else if (type === 'postgresql') {
|
|
generateEnvs = {
|
|
POSTGRESQL_PASSWORD: passwords[0],
|
|
POSTGRESQL_USERNAME: usernames[0],
|
|
POSTGRESQL_DATABASE: defaultDatabaseName
|
|
}
|
|
image = 'bitnami/postgresql:13.2.0'
|
|
volume = `${configuration.general.deployId}-${type}-data:/bitnami/postgresql`
|
|
} else if (type === 'couchdb') {
|
|
generateEnvs = {
|
|
COUCHDB_PASSWORD: passwords[0],
|
|
COUCHDB_USER: usernames[0]
|
|
}
|
|
image = 'bitnami/couchdb:3'
|
|
volume = `${configuration.general.deployId}-${type}-data:/bitnami/couchdb`
|
|
} else if (type === 'mysql') {
|
|
generateEnvs = {
|
|
MYSQL_ROOT_PASSWORD: passwords[0],
|
|
MYSQL_ROOT_USER: usernames[0],
|
|
MYSQL_USER: usernames[1],
|
|
MYSQL_PASSWORD: passwords[1],
|
|
MYSQL_DATABASE: defaultDatabaseName
|
|
}
|
|
image = 'bitnami/mysql:8.0'
|
|
volume = `${configuration.general.deployId}-${type}-data:/bitnami/mysql/data`
|
|
} else if (type === 'clickhouse') {
|
|
image = 'yandex/clickhouse-server'
|
|
volume = `${configuration.general.deployId}-${type}-data:/var/lib/clickhouse`
|
|
ulimits = {
|
|
nofile: {
|
|
soft: 262144,
|
|
hard: 262144
|
|
}
|
|
}
|
|
}
|
|
|
|
const stack = {
|
|
version: '3.8',
|
|
services: {
|
|
[configuration.general.deployId]: {
|
|
image,
|
|
networks: [`${docker.network}`],
|
|
environment: generateEnvs,
|
|
volumes: [volume],
|
|
ulimits,
|
|
deploy: {
|
|
replicas: 1,
|
|
update_config: {
|
|
parallelism: 0,
|
|
delay: '10s',
|
|
order: 'start-first'
|
|
},
|
|
rollback_config: {
|
|
parallelism: 0,
|
|
delay: '10s',
|
|
order: 'start-first'
|
|
},
|
|
labels: [
|
|
'managedBy=coolify',
|
|
'type=database',
|
|
'configuration=' + JSON.stringify(configuration)
|
|
]
|
|
}
|
|
}
|
|
},
|
|
networks: {
|
|
[`${docker.network}`]: {
|
|
external: true
|
|
}
|
|
},
|
|
volumes: {
|
|
[`${configuration.general.deployId}-${type}-data`]: {
|
|
external: true
|
|
}
|
|
}
|
|
}
|
|
await fs.writeFile(`${configuration.general.workdir}/stack.yml`, yaml.dump(stack))
|
|
await execShellAsync(
|
|
`cat ${configuration.general.workdir}/stack.yml | docker stack deploy -c - ${configuration.general.deployId}`
|
|
)
|
|
} catch (error) {
|
|
console.log(error)
|
|
await saveServerLog(error)
|
|
throw new Error(error)
|
|
}
|
|
})
|
|
|
|
fastify.delete('/:dbName', async (request, reply) => {
|
|
const { dbName } = request.params
|
|
await execShellAsync(`docker stack rm ${dbName}`)
|
|
reply.code(200).send({})
|
|
})
|
|
}
|