diff --git a/apps/api/src/lib.ts b/apps/api/src/lib.ts index 7fd158b2f..01bdfd2a9 100644 --- a/apps/api/src/lib.ts +++ b/apps/api/src/lib.ts @@ -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 } } } }) + } } \ No newline at end of file diff --git a/apps/api/src/lib/templates.ts b/apps/api/src/lib/templates.ts index a9cb8b925..51084662e 100644 --- a/apps/api/src/lib/templates.ts +++ b/apps/api/src/lib/templates.ts @@ -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": "", - }, + } ] }, { diff --git a/apps/api/src/routes/api/v1/services/handlers.ts b/apps/api/src/routes/api/v1/services/handlers.ts index a0149bc26..e65c4cb5c 100644 --- a/apps/api/src/routes/api/v1/services/handlers.ts +++ b/apps/api/src/routes/api/v1/services/handlers.ts @@ -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)) diff --git a/apps/api/src/routes/webhooks/traefik/handlers.ts b/apps/api/src/routes/webhooks/traefik/handlers.ts index 6d8747845..be579f2e9 100644 --- a/apps/api/src/routes/webhooks/traefik/handlers.ts +++ b/apps/api/src/routes/webhooks/traefik/handlers.ts @@ -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 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 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, diff --git a/apps/ui/src/routes/services/[id]/index.svelte b/apps/ui/src/routes/services/[id]/index.svelte index b604427c2..1ebb4a08d 100644 --- a/apps/ui/src/routes/services/[id]/index.svelte +++ b/apps/ui/src/routes/services/[id]/index.svelte @@ -259,15 +259,6 @@ - {#if service.type === 'minio' && !service.minio.apiFqdn && $status.service.isRunning} -
- IMPORTANT! 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. -
- {/if} -
@@ -307,58 +298,24 @@
- {#if service.type === 'minio'} -
- - - -
-
- - -
- {:else} -
- - -
- {/if} +
+ + +
{#if forceSave}
@@ -449,6 +406,15 @@ id={variable.name} value={service.fqdn} /> + {:else if variable.defaultValue === '$$generate_domain'} + {:else if variable.defaultValue === 'true' || variable.defaultValue === 'false'}