vscodeserver + minio

This commit is contained in:
Andras Bacsai 2022-10-18 14:34:10 +02:00
parent 12a1aeb0f8
commit f1ea01e709
5 changed files with 161 additions and 144 deletions

View File

@ -1,4 +1,4 @@
import { decrypt, encrypt, generatePassword, prisma } from "./lib/common";
import { decrypt, encrypt, prisma } from "./lib/common";
import { includeServices } from "./lib/services/common";
@ -10,36 +10,27 @@ export async function migrateServicesToNewTemplate() {
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)
if (service.type === 'vscode' && service.vscodeserver) await vscodeserver(service)
}
} catch (error) {
console.log(error)
}
}
async function migrateSettings(settings: any[], service: any) {
for (const setting of settings) {
if (!setting) continue;
const [name, value] = setting.split('@@@')
console.log('Migrating setting', name, value)
await prisma.serviceSetting.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
}
async function migrateSecrets(secrets: any[], service: any) {
for (const secret of secrets) {
if (!secret) continue;
const [name, value] = secret.split('@@@')
console.log('Migrating secret', name, value)
await prisma.serviceSecret.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSecret.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
}
async function createVolumes(volumes: any[], service: any) {
for (const volume of volumes) {
const [volumeName, path, containerId] = volume.split('@@@')
await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: service.id } }) || await prisma.servicePersistentStorage.create({ data: { volumeName, path, containerId, predefined: true, service: { connect: { id: service.id } } } })
}
async function vscodeserver(service: any) {
const { password } = service.minio
const secrets = [
`PASSWORD@@@${password}`,
]
await migrateSecrets(secrets, service);
// Remove old service data
// await prisma.service.update({ where: { id: service.id }, data: { vscodeserver: { delete: true } } })
}
async function minio(service: any) {
const { rootUser, rootUserPassword, apiFqdn } = service.fider
const { rootUser, rootUserPassword, apiFqdn } = service.minio
const secrets = [
`MINIO_ROOT_PASSWORD@@@${rootUserPassword}`,
@ -55,7 +46,6 @@ async function minio(service: any) {
// Remove old service data
// await prisma.service.update({ where: { id: service.id }, data: { minio: { delete: true } } })
}
async function fider(service: any) {
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, jwtSecret, emailNoreply, emailMailgunApiKey, emailMailgunDomain, emailMailgunRegion, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpEnableStartTls } = service.fider
@ -83,7 +73,7 @@ async function fider(service: any) {
await migrateSecrets(secrets, service);
// Remove old service data
await prisma.service.update({ where: { id: service.id }, data: { fider: { delete: true } } })
// await prisma.service.update({ where: { id: service.id }, data: { fider: { delete: true } } })
}
async function plausibleAnalytics(service: any) {
@ -114,5 +104,28 @@ async function plausibleAnalytics(service: any) {
await createVolumes(volumes, service);
// Remove old service data
await prisma.service.update({ where: { id: service.id }, data: { plausibleAnalytics: { delete: true } } })
// await prisma.service.update({ where: { id: service.id }, data: { plausibleAnalytics: { delete: true } } })
}
async function migrateSettings(settings: any[], service: any) {
for (const setting of settings) {
if (!setting) continue;
const [name, value] = setting.split('@@@')
console.log('Migrating setting', name, value)
await prisma.serviceSetting.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
}
async function migrateSecrets(secrets: any[], service: any) {
for (const secret of secrets) {
if (!secret) continue;
const [name, value] = secret.split('@@@')
console.log('Migrating secret', name, value)
await prisma.serviceSecret.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSecret.create({ data: { name, value, service: { connect: { id: service.id } } } })
}
}
async function createVolumes(volumes: any[], service: any) {
for (const volume of volumes) {
const [volumeName, path, containerId] = volume.split('@@@')
await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: service.id } }) || await prisma.servicePersistentStorage.create({ data: { volumeName, path, containerId, predefined: true, service: { connect: { id: service.id } } } })
}
}

View File

@ -1,4 +1,41 @@
export default [
{
"templateVersion": "1.0.0",
"serviceDefaultVersion": "4.7.1",
"name": "codeserver",
"displayName": "Code Server",
"description": "code-server by Coder is VS Code running on a remote server, accessible through the browser.",
"services": {
"$$id": {
"name": "Code Server",
"documentation": "Taken from https://github.com/coder/code-server/. ",
"depends_on": [],
"image": "codercom/code-server:$$core_version",
"volumes": [
"$$id-config-data:/home/coder/.local/share/code-server",
"$$id-vscodeserver-data:/home/coder",
"$$id-keys-directory:/root/.ssh",
"$$id-theme-and-plugin-directory:/root/.local/share/code-server"
],
"environment": [
"PASSWORD=$$secret_password",
],
"ports": [
"8080"
]
}
},
"variables": [
{
"id": "$$secret_password",
"name": "PASSWORD",
"label": "Password",
"defaultValue": "$$generate_password",
"description": ""
}
]
},
{
"templateVersion": "1.0.0",
"serviceDefaultVersion": "RELEASE.2022-10-15T19-57-03Z",
@ -20,8 +57,7 @@ export default [
"MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url",
"MINIO_DOMAIN=$$config_minio_domain",
"MINIO_ROOT_USER=$$config_minio_root_user",
"MINIO_ROOT_PASSWORD=$$secret_minio_root_user_password",
"MINIO_REGION_NAME=$$config_minio_region_name",
"MINIO_ROOT_PASSWORD=$$secret_minio_root_user_password"
],
"ports": [
"9001",
@ -33,9 +69,12 @@ export default [
{
"id": "$$config_server_url",
"name": "MINIO_SERVER_URL",
"label": "Server URL",
"label": "Server/Console URL",
"defaultValue": "",
"description": "",
"extras": {
"required": true
}
},
{
"id": "$$config_browser_redirect_url",
@ -64,14 +103,7 @@ export default [
"label": "Root User Password",
"defaultValue": "$$generate_password",
"description": "",
},
{
"id": "$$config_minio_region_name",
"name": "MINIO_REGION_NAME",
"label": "Region Name",
"defaultValue": "us-east-1",
"description": "",
},
}
]
},
{

View File

@ -128,10 +128,10 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
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 isVisibleOnUI = foundTemplate.variables.find(v => v.name === envKey)?.extras?.isVisibleOnUI
if (envValue.startsWith('$$config') || isVisibleOnUI) {
const extras = foundTemplate.variables.find(v => v.name === envKey)?.extras
if (envValue.startsWith('$$config')) {
parsedTemplate[realKey].environment.push(
{ name: envKey, value: envValue, label, description, defaultValue }
{ name: envKey, value: envValue, label, description, defaultValue, extras }
)
}
@ -155,6 +155,8 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
const { name, value } = setting
if (service.fqdn && value === '$$generate_fqdn') {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, service.fqdn))
} else if (service.fqdn && value === '$$generate_domain') {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, getDomain(service.fqdn)))
} else {
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, value))

View File

@ -359,12 +359,12 @@ export async function traefikConfiguration(request, reply) {
let otherNakedDomain = null;
let otherIsHttps = null;
let otherIsWWW = null;
if (type === 'minio' && service.minio.apiFqdn) {
otherDomain = getDomain(service.minio.apiFqdn);
if (type === 'minio') {
const domain = service.serviceSetting.find((a) => a.name === 'MINIO_SERVER_URL')?.value
otherDomain = getDomain(domain);
otherNakedDomain = otherDomain.replace(/^www\./, '');
otherIsHttps = service.minio.apiFqdn.startsWith('https://');
otherIsWWW = service.minio.apiFqdn.includes('www.');
otherIsHttps = domain.startsWith('https://');
otherIsWWW = domain.includes('www.');
}
data.services.push({
id,
@ -480,46 +480,42 @@ export async function traefikOtherConfiguration(request: FastifyRequest<TraefikO
} else if (type === 'http') {
const service = await prisma.service.findFirst({
where: { id },
include: { minio: true }
include: { serviceSetting: true }
});
if (service) {
if (service.type === 'minio') {
if (service?.minio?.apiFqdn) {
const {
minio: { apiFqdn }
} = service;
const domain = getDomain(apiFqdn);
const isHttps = apiFqdn.startsWith('https://');
traefik = {
[type]: {
routers: {
[id]: {
entrypoints: [type],
rule: `Host(\`${domain}\`)`,
service: id
}
},
services: {
[id]: {
loadbalancer: {
servers: [{ url: `http://${id}:${privatePort}` }]
}
const domainSetting = service.serviceSetting.find((a) => a.name === 'MINIO_SERVER_URL')?.value
const domain = getDomain(domainSetting);
const isHttps = domainSetting.startsWith('https://');
traefik = {
[type]: {
routers: {
[id]: {
entrypoints: [type],
rule: `Host(\`${domain}\`)`,
service: id
}
},
services: {
[id]: {
loadbalancer: {
servers: [{ url: `http://${id}:${privatePort}` }]
}
}
}
};
if (isHttps) {
if (isDev) {
traefik[type].routers[id].tls = {
domains: {
main: `${domain}`
}
};
} else {
traefik[type].routers[id].tls = {
certresolver: 'letsencrypt'
};
}
}
};
if (isHttps) {
if (isDev) {
traefik[type].routers[id].tls = {
domains: {
main: `${domain}`
}
};
} else {
traefik[type].routers[id].tls = {
certresolver: 'letsencrypt'
};
}
}
} else {
@ -758,11 +754,18 @@ export async function remoteTraefikConfiguration(request: FastifyRequest<OnlyId>
let otherIsHttps = null;
let otherIsWWW = null;
if (type === 'minio' && service.minio.apiFqdn) {
otherDomain = getDomain(service.minio.apiFqdn);
// if (type === 'minio' && service.minio.apiFqdn) {
// otherDomain = getDomain(service.minio.apiFqdn);
// otherNakedDomain = otherDomain.replace(/^www\./, '');
// otherIsHttps = service.minio.apiFqdn.startsWith('https://');
// otherIsWWW = service.minio.apiFqdn.includes('www.');
// }
if (type === 'minio') {
const domain = service.serviceSetting.find((a) => a.name === 'MINIO_SERVER_URL')?.value
otherDomain = getDomain(domain);
otherNakedDomain = otherDomain.replace(/^www\./, '');
otherIsHttps = service.minio.apiFqdn.startsWith('https://');
otherIsWWW = service.minio.apiFqdn.includes('www.');
otherIsHttps = domain.startsWith('https://');
otherIsWWW = domain.includes('www.');
}
data.services.push({
id,

View File

@ -259,15 +259,6 @@
</div>
</div>
{#if service.type === 'minio' && !service.minio.apiFqdn && $status.service.isRunning}
<div class="py-5">
<span class="font-bold text-red-500">IMPORTANT!</span> There was a small modification with Minio
in the latest version of Coolify. Now you can separate the Console URL from the API URL, so you
could use both through SSL. But this proccess cannot be done automatically, so you have to stop
your Minio instance, configure the new domain and start it back. Sorry for any inconvenience.
</div>
{/if}
<div class="grid grid-flow-row gap-2 px-4">
<div class="mt-2 grid grid-cols-2 items-center">
<label for="name">{$t('forms.name')}</label>
@ -307,58 +298,24 @@
</div>
</div>
{#if service.type === 'minio'}
<div class="grid grid-cols-2 items-center">
<label for="fqdn"
>Console URL <Explainer explanation={$t('application.https_explainer')} /></label
>
<CopyPasswordField
placeholder="eg: https://console.min.io"
readonly={isDisabled}
disabled={isDisabled}
name="fqdn"
id="fqdn"
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
bind:value={service.fqdn}
required
/>
</div>
<div class="grid grid-cols-2 items-center">
<label for="apiFqdn"
>API URL <Explainer explanation={$t('application.https_explainer')} /></label
>
<CopyPasswordField
placeholder="eg: https://min.io"
readonly={!$appSession.isAdmin && !$status.service.isRunning}
disabled={isDisabled}
name="apiFqdn"
id="apiFqdn"
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
bind:value={service.minio.apiFqdn}
required
/>
</div>
{:else}
<div class="grid grid-cols-2 items-center">
<label for="fqdn"
>{$t('application.url_fqdn')}
<Explainer explanation={$t('application.https_explainer')} />
</label>
<CopyPasswordField
placeholder="eg: https://analytics.coollabs.io"
readonly={!$appSession.isAdmin && !$status.service.isRunning}
disabled={!$appSession.isAdmin ||
$status.service.isRunning ||
$status.service.initialLoading}
name="fqdn"
id="fqdn"
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
bind:value={service.fqdn}
required
/>
</div>
{/if}
<div class="grid grid-cols-2 items-center">
<label for="fqdn"
>{$t('application.url_fqdn')}
<Explainer explanation={$t('application.https_explainer')} />
</label>
<CopyPasswordField
placeholder="eg: https://analytics.coollabs.io"
readonly={!$appSession.isAdmin && !$status.service.isRunning}
disabled={!$appSession.isAdmin ||
$status.service.isRunning ||
$status.service.initialLoading}
name="fqdn"
id="fqdn"
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
bind:value={service.fqdn}
required
/>
</div>
</div>
{#if forceSave}
<div class="flex-col space-y-2 pt-4 text-center">
@ -449,6 +406,15 @@
id={variable.name}
value={service.fqdn}
/>
{:else if variable.defaultValue === '$$generate_domain'}
<input
class="w-full"
disabled
readonly
name={variable.name}
id={variable.name}
value={getDomain(service.fqdn)}
/>
{:else if variable.defaultValue === 'true' || variable.defaultValue === 'false'}
<select
class="w-full font-normal"
@ -473,6 +439,7 @@
/>
{:else}
<CopyPasswordField
required={variable?.extras?.required}
readonly={isDisabled}
disabled={isDisabled}
name={variable.name}