vscodeserver + minio
This commit is contained in:
@@ -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";
|
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 === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service)
|
||||||
if (service.type === 'fider' && service.fider) await fider(service)
|
if (service.type === 'fider' && service.fider) await fider(service)
|
||||||
if (service.type === 'minio' && service.minio) await minio(service)
|
if (service.type === 'minio' && service.minio) await minio(service)
|
||||||
|
if (service.type === 'vscode' && service.vscodeserver) await vscodeserver(service)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function migrateSettings(settings: any[], service: any) {
|
|
||||||
for (const setting of settings) {
|
async function vscodeserver(service: any) {
|
||||||
if (!setting) continue;
|
const { password } = service.minio
|
||||||
const [name, value] = setting.split('@@@')
|
|
||||||
console.log('Migrating setting', name, value)
|
const secrets = [
|
||||||
await prisma.serviceSetting.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name, value, service: { connect: { id: service.id } } } })
|
`PASSWORD@@@${password}`,
|
||||||
}
|
]
|
||||||
}
|
await migrateSecrets(secrets, service);
|
||||||
async function migrateSecrets(secrets: any[], service: any) {
|
|
||||||
for (const secret of secrets) {
|
// Remove old service data
|
||||||
if (!secret) continue;
|
// await prisma.service.update({ where: { id: service.id }, data: { vscodeserver: { delete: true } } })
|
||||||
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 minio(service: any) {
|
async function minio(service: any) {
|
||||||
const { rootUser, rootUserPassword, apiFqdn } = service.fider
|
const { rootUser, rootUserPassword, apiFqdn } = service.minio
|
||||||
|
|
||||||
const secrets = [
|
const secrets = [
|
||||||
`MINIO_ROOT_PASSWORD@@@${rootUserPassword}`,
|
`MINIO_ROOT_PASSWORD@@@${rootUserPassword}`,
|
||||||
@@ -55,7 +46,6 @@ async function minio(service: any) {
|
|||||||
|
|
||||||
// Remove old service data
|
// Remove old service data
|
||||||
// await prisma.service.update({ where: { id: service.id }, data: { minio: { delete: true } } })
|
// await prisma.service.update({ where: { id: service.id }, data: { minio: { delete: true } } })
|
||||||
|
|
||||||
}
|
}
|
||||||
async function fider(service: any) {
|
async function fider(service: any) {
|
||||||
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, jwtSecret, emailNoreply, emailMailgunApiKey, emailMailgunDomain, emailMailgunRegion, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpEnableStartTls } = service.fider
|
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);
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
// Remove old service data
|
// 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) {
|
async function plausibleAnalytics(service: any) {
|
||||||
@@ -114,5 +104,28 @@ async function plausibleAnalytics(service: any) {
|
|||||||
await createVolumes(volumes, service);
|
await createVolumes(volumes, service);
|
||||||
|
|
||||||
// Remove old service data
|
// 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 } } } })
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,41 @@
|
|||||||
export default [
|
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",
|
"templateVersion": "1.0.0",
|
||||||
"serviceDefaultVersion": "RELEASE.2022-10-15T19-57-03Z",
|
"serviceDefaultVersion": "RELEASE.2022-10-15T19-57-03Z",
|
||||||
@@ -20,8 +57,7 @@ export default [
|
|||||||
"MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url",
|
"MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url",
|
||||||
"MINIO_DOMAIN=$$config_minio_domain",
|
"MINIO_DOMAIN=$$config_minio_domain",
|
||||||
"MINIO_ROOT_USER=$$config_minio_root_user",
|
"MINIO_ROOT_USER=$$config_minio_root_user",
|
||||||
"MINIO_ROOT_PASSWORD=$$secret_minio_root_user_password",
|
"MINIO_ROOT_PASSWORD=$$secret_minio_root_user_password"
|
||||||
"MINIO_REGION_NAME=$$config_minio_region_name",
|
|
||||||
],
|
],
|
||||||
"ports": [
|
"ports": [
|
||||||
"9001",
|
"9001",
|
||||||
@@ -33,9 +69,12 @@ export default [
|
|||||||
{
|
{
|
||||||
"id": "$$config_server_url",
|
"id": "$$config_server_url",
|
||||||
"name": "MINIO_SERVER_URL",
|
"name": "MINIO_SERVER_URL",
|
||||||
"label": "Server URL",
|
"label": "Server/Console URL",
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
"extras": {
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "$$config_browser_redirect_url",
|
"id": "$$config_browser_redirect_url",
|
||||||
@@ -64,14 +103,7 @@ export default [
|
|||||||
"label": "Root User Password",
|
"label": "Root User Password",
|
||||||
"defaultValue": "$$generate_password",
|
"defaultValue": "$$generate_password",
|
||||||
"description": "",
|
"description": "",
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"id": "$$config_minio_region_name",
|
|
||||||
"name": "MINIO_REGION_NAME",
|
|
||||||
"label": "Region Name",
|
|
||||||
"defaultValue": "us-east-1",
|
|
||||||
"description": "",
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -128,10 +128,10 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
|
|||||||
const label = foundTemplate.variables.find(v => v.name === envKey)?.label
|
const label = foundTemplate.variables.find(v => v.name === envKey)?.label
|
||||||
const description = foundTemplate.variables.find(v => v.name === envKey)?.description
|
const description = foundTemplate.variables.find(v => v.name === envKey)?.description
|
||||||
const defaultValue = foundTemplate.variables.find(v => v.name === envKey)?.defaultValue
|
const defaultValue = foundTemplate.variables.find(v => v.name === envKey)?.defaultValue
|
||||||
const isVisibleOnUI = foundTemplate.variables.find(v => v.name === envKey)?.extras?.isVisibleOnUI
|
const extras = foundTemplate.variables.find(v => v.name === envKey)?.extras
|
||||||
if (envValue.startsWith('$$config') || isVisibleOnUI) {
|
if (envValue.startsWith('$$config')) {
|
||||||
parsedTemplate[realKey].environment.push(
|
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
|
const { name, value } = setting
|
||||||
if (service.fqdn && value === '$$generate_fqdn') {
|
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(`$$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 {
|
} else {
|
||||||
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, value))
|
parsedTemplate = JSON.parse(JSON.stringify(parsedTemplate).replaceAll(`$$config_${name.toLowerCase()}`, value))
|
||||||
|
|
||||||
|
@@ -359,12 +359,12 @@ export async function traefikConfiguration(request, reply) {
|
|||||||
let otherNakedDomain = null;
|
let otherNakedDomain = null;
|
||||||
let otherIsHttps = null;
|
let otherIsHttps = null;
|
||||||
let otherIsWWW = null;
|
let otherIsWWW = null;
|
||||||
|
if (type === 'minio') {
|
||||||
if (type === 'minio' && service.minio.apiFqdn) {
|
const domain = service.serviceSetting.find((a) => a.name === 'MINIO_SERVER_URL')?.value
|
||||||
otherDomain = getDomain(service.minio.apiFqdn);
|
otherDomain = getDomain(domain);
|
||||||
otherNakedDomain = otherDomain.replace(/^www\./, '');
|
otherNakedDomain = otherDomain.replace(/^www\./, '');
|
||||||
otherIsHttps = service.minio.apiFqdn.startsWith('https://');
|
otherIsHttps = domain.startsWith('https://');
|
||||||
otherIsWWW = service.minio.apiFqdn.includes('www.');
|
otherIsWWW = domain.includes('www.');
|
||||||
}
|
}
|
||||||
data.services.push({
|
data.services.push({
|
||||||
id,
|
id,
|
||||||
@@ -480,16 +480,13 @@ export async function traefikOtherConfiguration(request: FastifyRequest<TraefikO
|
|||||||
} else if (type === 'http') {
|
} else if (type === 'http') {
|
||||||
const service = await prisma.service.findFirst({
|
const service = await prisma.service.findFirst({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: { minio: true }
|
include: { serviceSetting: true }
|
||||||
});
|
});
|
||||||
if (service) {
|
if (service) {
|
||||||
if (service.type === 'minio') {
|
if (service.type === 'minio') {
|
||||||
if (service?.minio?.apiFqdn) {
|
const domainSetting = service.serviceSetting.find((a) => a.name === 'MINIO_SERVER_URL')?.value
|
||||||
const {
|
const domain = getDomain(domainSetting);
|
||||||
minio: { apiFqdn }
|
const isHttps = domainSetting.startsWith('https://');
|
||||||
} = service;
|
|
||||||
const domain = getDomain(apiFqdn);
|
|
||||||
const isHttps = apiFqdn.startsWith('https://');
|
|
||||||
traefik = {
|
traefik = {
|
||||||
[type]: {
|
[type]: {
|
||||||
routers: {
|
routers: {
|
||||||
@@ -521,7 +518,6 @@ export async function traefikOtherConfiguration(request: FastifyRequest<TraefikO
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (service?.fqdn) {
|
if (service?.fqdn) {
|
||||||
const domain = getDomain(service.fqdn);
|
const domain = getDomain(service.fqdn);
|
||||||
@@ -758,11 +754,18 @@ export async function remoteTraefikConfiguration(request: FastifyRequest<OnlyId>
|
|||||||
let otherIsHttps = null;
|
let otherIsHttps = null;
|
||||||
let otherIsWWW = null;
|
let otherIsWWW = null;
|
||||||
|
|
||||||
if (type === 'minio' && service.minio.apiFqdn) {
|
// if (type === 'minio' && service.minio.apiFqdn) {
|
||||||
otherDomain = getDomain(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\./, '');
|
otherNakedDomain = otherDomain.replace(/^www\./, '');
|
||||||
otherIsHttps = service.minio.apiFqdn.startsWith('https://');
|
otherIsHttps = domain.startsWith('https://');
|
||||||
otherIsWWW = service.minio.apiFqdn.includes('www.');
|
otherIsWWW = domain.includes('www.');
|
||||||
}
|
}
|
||||||
data.services.push({
|
data.services.push({
|
||||||
id,
|
id,
|
||||||
|
@@ -259,15 +259,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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="grid grid-flow-row gap-2 px-4">
|
||||||
<div class="mt-2 grid grid-cols-2 items-center">
|
<div class="mt-2 grid grid-cols-2 items-center">
|
||||||
<label for="name">{$t('forms.name')}</label>
|
<label for="name">{$t('forms.name')}</label>
|
||||||
@@ -307,39 +298,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="fqdn"
|
<label for="fqdn"
|
||||||
>{$t('application.url_fqdn')}
|
>{$t('application.url_fqdn')}
|
||||||
@@ -358,7 +316,6 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{#if forceSave}
|
{#if forceSave}
|
||||||
<div class="flex-col space-y-2 pt-4 text-center">
|
<div class="flex-col space-y-2 pt-4 text-center">
|
||||||
@@ -449,6 +406,15 @@
|
|||||||
id={variable.name}
|
id={variable.name}
|
||||||
value={service.fqdn}
|
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'}
|
{:else if variable.defaultValue === 'true' || variable.defaultValue === 'false'}
|
||||||
<select
|
<select
|
||||||
class="w-full font-normal"
|
class="w-full font-normal"
|
||||||
@@ -473,6 +439,7 @@
|
|||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
required={variable?.extras?.required}
|
||||||
readonly={isDisabled}
|
readonly={isDisabled}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
name={variable.name}
|
name={variable.name}
|
||||||
|
Reference in New Issue
Block a user