mirror of
https://github.com/cupcakearmy/coolify.git
synced 2024-10-23 00:24:15 +02:00
fix + cleanup
This commit is contained in:
parent
1225786fc0
commit
0d12f3043b
@ -2204,6 +2204,7 @@
|
|||||||
Server, SQLite & MariaDB into a smart-spreadsheet.
|
Server, SQLite & MariaDB into a smart-spreadsheet.
|
||||||
services:
|
services:
|
||||||
$$id:
|
$$id:
|
||||||
|
name: NocoDB
|
||||||
image: nocodb/nocodb:$$core_version
|
image: nocodb/nocodb:$$core_version
|
||||||
environment:
|
environment:
|
||||||
- PORT=$$config_port
|
- PORT=$$config_port
|
||||||
|
@ -1091,7 +1091,7 @@ export const createDirectories = async ({
|
|||||||
repository: string;
|
repository: string;
|
||||||
buildId: string;
|
buildId: string;
|
||||||
}): Promise<{ workdir: string; repodir: string }> => {
|
}): Promise<{ workdir: string; repodir: string }> => {
|
||||||
repository = repository.replaceAll(' ','')
|
repository = repository.replaceAll(' ', '')
|
||||||
const repodir = `/tmp/build-sources/${repository}/`;
|
const repodir = `/tmp/build-sources/${repository}/`;
|
||||||
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
|
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
|
||||||
let workdirFound = false;
|
let workdirFound = false;
|
||||||
|
@ -35,7 +35,6 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const service = await getServiceFromDB({ id, teamId });
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
const arm = isARM(service.arch)
|
const arm = isARM(service.arch)
|
||||||
console.log(arm)
|
|
||||||
const { type, destinationDockerId, destinationDocker, persistentStorage } =
|
const { type, destinationDockerId, destinationDocker, persistentStorage } =
|
||||||
service;
|
service;
|
||||||
|
|
||||||
@ -76,6 +75,16 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const customVolumes = await prisma.servicePersistentStorage.findMany({ where: { serviceId: service } })
|
||||||
|
let volumes = arm ? template.services[service].volumesArm : template.services[service].volumes
|
||||||
|
if (customVolumes.length > 0) {
|
||||||
|
for (const customVolume of customVolumes) {
|
||||||
|
const { volumeName, path } = customVolume
|
||||||
|
if (!volumes.includes(`${volumeName}:${path}`)) {
|
||||||
|
volumes.push(`${volumeName}:${path}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
config[service] = {
|
config[service] = {
|
||||||
container_name: service,
|
container_name: service,
|
||||||
build: template.services[service].build || undefined,
|
build: template.services[service].build || undefined,
|
||||||
@ -84,7 +93,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
image: arm ? template.services[service].imageArm : template.services[service].image,
|
image: arm ? template.services[service].imageArm : template.services[service].image,
|
||||||
expose: template.services[service].ports,
|
expose: template.services[service].ports,
|
||||||
// ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
// ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
volumes: arm ? template.services[service].volumesArm : template.services[service].volumes,
|
volumes,
|
||||||
environment: newEnvironments,
|
environment: newEnvironments,
|
||||||
depends_on: template.services[service]?.depends_on,
|
depends_on: template.services[service]?.depends_on,
|
||||||
ulimits: template.services[service]?.ulimits,
|
ulimits: template.services[service]?.ulimits,
|
||||||
@ -93,7 +102,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
labels: makeLabelForServices(type),
|
labels: makeLabelForServices(type),
|
||||||
...defaultComposeConfiguration(network),
|
...defaultComposeConfiguration(network),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate files for builds
|
// Generate files for builds
|
||||||
if (template.services[service]?.files?.length > 0) {
|
if (template.services[service]?.files?.length > 0) {
|
||||||
if (!template.services[service].build) {
|
if (!template.services[service].build) {
|
||||||
@ -113,7 +122,6 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
await fs.writeFile(`${workdir}/Dockerfile.${service}`, Dockerfile);
|
await fs.writeFile(`${workdir}/Dockerfile.${service}`, Dockerfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
Example of a supported version:
|
|
||||||
{
|
|
||||||
// Name used to identify the service internally
|
|
||||||
name: 'umami',
|
|
||||||
// Fancier name to show to the user
|
|
||||||
fancyName: 'Umami',
|
|
||||||
// Docker base image for the service
|
|
||||||
baseImage: 'ghcr.io/mikecao/umami',
|
|
||||||
// Optional: If there is any dependent image, you should list it here
|
|
||||||
images: [],
|
|
||||||
// Usable tags
|
|
||||||
versions: ['postgresql-latest'],
|
|
||||||
// Which tag is the recommended
|
|
||||||
recommendedVersion: 'postgresql-latest',
|
|
||||||
// Application's default port, Umami listens on 3000
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
export const supportedServiceTypesAndVersions = [
|
|
||||||
{
|
|
||||||
name: 'plausibleanalytics',
|
|
||||||
fancyName: 'Plausible Analytics',
|
|
||||||
baseImage: 'plausible/analytics',
|
|
||||||
images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
|
|
||||||
versions: ['latest', 'stable'],
|
|
||||||
recommendedVersion: 'stable',
|
|
||||||
ports: {
|
|
||||||
main: 8000
|
|
||||||
},
|
|
||||||
labels: ['analytics', 'plausible', 'plausible-analytics', 'gdpr', 'no-cookie']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nocodb',
|
|
||||||
fancyName: 'NocoDB',
|
|
||||||
baseImage: 'nocodb/nocodb',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['nocodb', 'airtable', 'database']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'minio',
|
|
||||||
fancyName: 'MinIO',
|
|
||||||
baseImage: 'minio/minio',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 9001
|
|
||||||
},
|
|
||||||
labels: ['minio', 's3', 'storage']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vscodeserver',
|
|
||||||
fancyName: 'VSCode Server',
|
|
||||||
baseImage: 'codercom/code-server',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['vscodeserver', 'vscode', 'code-server', 'ide']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wordpress',
|
|
||||||
fancyName: 'WordPress',
|
|
||||||
baseImage: 'wordpress',
|
|
||||||
images: ['bitnami/mysql:5.7'],
|
|
||||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
},
|
|
||||||
labels: ['wordpress', 'blog', 'cms']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vaultwarden',
|
|
||||||
fancyName: 'Vaultwarden',
|
|
||||||
baseImage: 'vaultwarden/server',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
},
|
|
||||||
labels: ['vaultwarden', 'password-manager', 'passwords']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languagetool',
|
|
||||||
fancyName: 'LanguageTool',
|
|
||||||
baseImage: 'silviof/docker-languagetool',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8010
|
|
||||||
},
|
|
||||||
labels: ['languagetool', 'grammar', 'spell-checker']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'n8n',
|
|
||||||
fancyName: 'n8n',
|
|
||||||
baseImage: 'n8nio/n8n',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 5678
|
|
||||||
},
|
|
||||||
labels: ['n8n', 'workflow', 'automation', 'ifttt', 'zapier', 'nodered']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'uptimekuma',
|
|
||||||
fancyName: 'Uptime Kuma',
|
|
||||||
baseImage: 'louislam/uptime-kuma',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 3001
|
|
||||||
},
|
|
||||||
labels: ['uptimekuma', 'uptime', 'monitoring']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ghost',
|
|
||||||
fancyName: 'Ghost',
|
|
||||||
baseImage: 'bitnami/ghost',
|
|
||||||
images: ['bitnami/mariadb'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 2368
|
|
||||||
},
|
|
||||||
labels: ['ghost', 'blog', 'cms']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'meilisearch',
|
|
||||||
fancyName: 'Meilisearch',
|
|
||||||
baseImage: 'getmeili/meilisearch',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 7700
|
|
||||||
},
|
|
||||||
labels: ['meilisearch', 'search', 'search-engine']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'umami',
|
|
||||||
fancyName: 'Umami',
|
|
||||||
baseImage: 'ghcr.io/umami-software/umami',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['postgresql-latest'],
|
|
||||||
recommendedVersion: 'postgresql-latest',
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
},
|
|
||||||
labels: ['umami', 'analytics', 'gdpr', 'no-cookie']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'hasura',
|
|
||||||
fancyName: 'Hasura',
|
|
||||||
baseImage: 'hasura/graphql-engine',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['latest', 'v2.10.0', 'v2.5.1'],
|
|
||||||
recommendedVersion: 'v2.10.0',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['hasura', 'graphql', 'database']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fider',
|
|
||||||
fancyName: 'Fider',
|
|
||||||
baseImage: 'getfider/fider',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['stable'],
|
|
||||||
recommendedVersion: 'stable',
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
},
|
|
||||||
labels: ['fider', 'feedback', 'suggestions']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'appwrite',
|
|
||||||
fancyName: 'Appwrite',
|
|
||||||
baseImage: 'appwrite/appwrite',
|
|
||||||
images: ['mariadb:10.7', 'redis:6.2-alpine', 'appwrite/telegraf:1.4.0'],
|
|
||||||
versions: ['latest', '1.0', '0.15.3'],
|
|
||||||
recommendedVersion: '1.0',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
},
|
|
||||||
labels: ['appwrite', 'database', 'storage', 'api', 'serverless']
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: 'moodle',
|
|
||||||
// fancyName: 'Moodle',
|
|
||||||
// baseImage: 'bitnami/moodle',
|
|
||||||
// images: [],
|
|
||||||
// versions: ['latest', 'v4.0.2'],
|
|
||||||
// recommendedVersion: 'latest',
|
|
||||||
// ports: {
|
|
||||||
// main: 8080
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
{
|
|
||||||
name: 'glitchTip',
|
|
||||||
fancyName: 'GlitchTip',
|
|
||||||
baseImage: 'glitchtip/glitchtip',
|
|
||||||
images: ['postgres:14-alpine', 'redis:7-alpine'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8000
|
|
||||||
},
|
|
||||||
labels: ['glitchtip', 'error-reporting', 'error', 'sentry', 'bugsnag']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'searxng',
|
|
||||||
fancyName: 'SearXNG',
|
|
||||||
baseImage: 'searxng/searxng',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['searxng', 'search', 'search-engine']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'weblate',
|
|
||||||
fancyName: 'Weblate',
|
|
||||||
baseImage: 'weblate/weblate',
|
|
||||||
images: ['postgres:14-alpine', 'redis:6-alpine'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['weblate', 'translation', 'localization']
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: 'taiga',
|
|
||||||
// fancyName: 'Taiga',
|
|
||||||
// baseImage: 'taigaio/taiga-front',
|
|
||||||
// images: ['postgres:12.3', 'rabbitmq:3.8-management-alpine', 'taigaio/taiga-back', 'taigaio/taiga-events', 'taigaio/taiga-protected'],
|
|
||||||
// versions: ['latest'],
|
|
||||||
// recommendedVersion: 'latest',
|
|
||||||
// ports: {
|
|
||||||
// main: 80
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
name: 'grafana',
|
|
||||||
fancyName: 'Grafana',
|
|
||||||
baseImage: 'grafana/grafana',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest', '9.1.3', '9.1.2', '9.0.8', '8.3.11', '8.4.11', '8.5.11'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
},
|
|
||||||
labels: ['grafana', 'monitoring', 'metrics', 'dashboard']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'trilium',
|
|
||||||
fancyName: 'Trilium Notes',
|
|
||||||
baseImage: 'zadam/trilium',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
},
|
|
||||||
labels: ['trilium', 'notes', 'note-taking', 'wiki']
|
|
||||||
},
|
|
||||||
];
|
|
@ -15,7 +15,6 @@ import {
|
|||||||
uniqueName,
|
uniqueName,
|
||||||
version,
|
version,
|
||||||
} from "../../../lib/common";
|
} from "../../../lib/common";
|
||||||
import { supportedServiceTypesAndVersions } from "../../../lib/services/supportedVersions";
|
|
||||||
import { scheduler } from "../../../lib/scheduler";
|
import { scheduler } from "../../../lib/scheduler";
|
||||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||||
import type { Login, Update } from ".";
|
import type { Login, Update } from ".";
|
||||||
@ -382,7 +381,6 @@ export async function getCurrentUser(
|
|||||||
return {
|
return {
|
||||||
settings: await prisma.setting.findFirst(),
|
settings: await prisma.setting.findFirst(),
|
||||||
pendingInvitations,
|
pendingInvitations,
|
||||||
supportedServiceTypesAndVersions,
|
|
||||||
token,
|
token,
|
||||||
...request.user,
|
...request.user,
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,6 @@ import cuid from 'cuid';
|
|||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetGlitchTipSettings, SetWordpressSettings } from './types';
|
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetGlitchTipSettings, SetWordpressSettings } from './types';
|
||||||
import { supportedServiceTypesAndVersions } from '../../../../lib/services/supportedVersions';
|
|
||||||
import { configureServiceType, removeService } from '../../../../lib/services/common';
|
import { configureServiceType, removeService } from '../../../../lib/services/common';
|
||||||
import { hashPassword } from '../handlers';
|
import { hashPassword } from '../handlers';
|
||||||
import { getTemplates } from '../../../../lib/services';
|
import { getTemplates } from '../../../../lib/services';
|
||||||
@ -316,19 +315,7 @@ export async function saveServiceType(request: FastifyRequest<SaveServiceType>,
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getServiceVersions(request: FastifyRequest<OnlyId>) {
|
|
||||||
try {
|
|
||||||
const teamId = request.user.teamId;
|
|
||||||
const { id } = request.params;
|
|
||||||
const { type } = await getServiceFromDB({ id, teamId });
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
versions: supportedServiceTypesAndVersions.find((name) => name.name === type).versions
|
|
||||||
}
|
|
||||||
} catch ({ status, message }) {
|
|
||||||
return errorHandler({ status, message })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export async function saveServiceVersion(request: FastifyRequest<SaveServiceVersion>, reply: FastifyReply) {
|
export async function saveServiceVersion(request: FastifyRequest<SaveServiceVersion>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
@ -601,16 +588,21 @@ export async function getServiceStorages(request: FastifyRequest<OnlyId>) {
|
|||||||
export async function saveServiceStorage(request: FastifyRequest<SaveServiceStorage>, reply: FastifyReply) {
|
export async function saveServiceStorage(request: FastifyRequest<SaveServiceStorage>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const { path, newStorage, storageId } = request.body
|
const { path, isNewStorage, storageId, containerId } = request.body
|
||||||
|
|
||||||
if (newStorage) {
|
if (isNewStorage) {
|
||||||
|
const volumeName = `${id}-custom${path.replace(/\//gi, '-')}`
|
||||||
|
const found = await prisma.servicePersistentStorage.findFirst({ where: { path, containerId } });
|
||||||
|
if (found) {
|
||||||
|
throw { status: 500, message: 'Persistent storage already exists for this container and path.' }
|
||||||
|
}
|
||||||
await prisma.servicePersistentStorage.create({
|
await prisma.servicePersistentStorage.create({
|
||||||
data: { path, service: { connect: { id } } }
|
data: { path, volumeName, containerId, service: { connect: { id } } }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await prisma.servicePersistentStorage.update({
|
await prisma.servicePersistentStorage.update({
|
||||||
where: { id: storageId },
|
where: { id: storageId },
|
||||||
data: { path }
|
data: { path, containerId }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
|
@ -16,7 +16,6 @@ import {
|
|||||||
getServiceStorages,
|
getServiceStorages,
|
||||||
getServiceType,
|
getServiceType,
|
||||||
getServiceUsage,
|
getServiceUsage,
|
||||||
getServiceVersions,
|
|
||||||
listServices,
|
listServices,
|
||||||
newService,
|
newService,
|
||||||
saveService,
|
saveService,
|
||||||
@ -64,7 +63,6 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/:id/configuration/type', async (request) => await getServiceType(request));
|
fastify.get('/:id/configuration/type', async (request) => await getServiceType(request));
|
||||||
fastify.post<SaveServiceType>('/:id/configuration/type', async (request, reply) => await saveServiceType(request, reply));
|
fastify.post<SaveServiceType>('/:id/configuration/type', async (request, reply) => await saveServiceType(request, reply));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id/configuration/version', async (request) => await getServiceVersions(request));
|
|
||||||
fastify.post<SaveServiceVersion>('/:id/configuration/version', async (request, reply) => await saveServiceVersion(request, reply));
|
fastify.post<SaveServiceVersion>('/:id/configuration/version', async (request, reply) => await saveServiceVersion(request, reply));
|
||||||
|
|
||||||
fastify.post<SaveServiceDestination>('/:id/configuration/destination', async (request, reply) => await saveServiceDestination(request, reply));
|
fastify.post<SaveServiceDestination>('/:id/configuration/destination', async (request, reply) => await saveServiceDestination(request, reply));
|
||||||
|
@ -66,8 +66,9 @@ export interface DeleteServiceSecret extends OnlyId {
|
|||||||
export interface SaveServiceStorage extends OnlyId {
|
export interface SaveServiceStorage extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
path: string,
|
path: string,
|
||||||
newStorage: string,
|
containerId: string,
|
||||||
storageId: string,
|
storageId: string,
|
||||||
|
isNewStorage: boolean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { FastifyRequest } from "fastify";
|
import { FastifyRequest } from "fastify";
|
||||||
import { errorHandler, getDomain, isDev, prisma, executeDockerCmd, fixType } from "../../../lib/common";
|
import { errorHandler, getDomain, isDev, prisma, executeDockerCmd, fixType } from "../../../lib/common";
|
||||||
import { supportedServiceTypesAndVersions } from "../../../lib/services/supportedVersions";
|
|
||||||
import { TraefikOtherConfiguration } from "./types";
|
import { TraefikOtherConfiguration } from "./types";
|
||||||
import { OnlyId } from "../../../types";
|
import { OnlyId } from "../../../types";
|
||||||
import { getTemplates } from "../../../lib/services";
|
import { getTemplates } from "../../../lib/services";
|
||||||
@ -873,7 +872,8 @@ export async function remoteTraefikConfiguration(request: FastifyRequest<OnlyId>
|
|||||||
plausibleAnalytics
|
plausibleAnalytics
|
||||||
} = service;
|
} = service;
|
||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
const templates = await getTemplates();
|
||||||
|
let found = templates.find((a) => fixType(a.name) === fixType(type));
|
||||||
if (found) {
|
if (found) {
|
||||||
const port = found.ports.main;
|
const port = found.ports.main;
|
||||||
const publicPort = service[type]?.publicPort;
|
const publicPort = service[type]?.publicPort;
|
||||||
|
@ -19,7 +19,6 @@ interface AppSession {
|
|||||||
github: string | null,
|
github: string | null,
|
||||||
gitlab: string | null,
|
gitlab: string | null,
|
||||||
},
|
},
|
||||||
supportedServiceTypesAndVersions: Array<any>
|
|
||||||
pendingInvitations: Array<any>
|
pendingInvitations: Array<any>
|
||||||
}
|
}
|
||||||
interface AddToast {
|
interface AddToast {
|
||||||
@ -48,7 +47,6 @@ export const appSession: Writable<AppSession> = writable({
|
|||||||
github: null,
|
github: null,
|
||||||
gitlab: null
|
gitlab: null
|
||||||
},
|
},
|
||||||
supportedServiceTypesAndVersions: [],
|
|
||||||
pendingInvitations: []
|
pendingInvitations: []
|
||||||
});
|
});
|
||||||
export const disabledButton: Writable<boolean> = writable(false);
|
export const disabledButton: Writable<boolean> = writable(false);
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let baseSettings: any;
|
export let baseSettings: any;
|
||||||
export let supportedServiceTypesAndVersions: any;
|
|
||||||
export let pendingInvitations: any = 0;
|
export let pendingInvitations: any = 0;
|
||||||
|
|
||||||
$appSession.isRegistrationEnabled = baseSettings.isRegistrationEnabled;
|
$appSession.isRegistrationEnabled = baseSettings.isRegistrationEnabled;
|
||||||
@ -74,7 +73,6 @@
|
|||||||
$appSession.version = baseSettings.version;
|
$appSession.version = baseSettings.version;
|
||||||
$appSession.whiteLabeled = baseSettings.whiteLabeled;
|
$appSession.whiteLabeled = baseSettings.whiteLabeled;
|
||||||
$appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon;
|
$appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon;
|
||||||
$appSession.supportedServiceTypesAndVersions = supportedServiceTypesAndVersions;
|
|
||||||
|
|
||||||
$appSession.pendingInvitations = pendingInvitations;
|
$appSession.pendingInvitations = pendingInvitations;
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let isNew = false;
|
export let isNew = false;
|
||||||
export let storage: any = {
|
export let storage: any = {};
|
||||||
id: null,
|
export let services: any = [];
|
||||||
path: null
|
|
||||||
};
|
|
||||||
import { del, post } from '$lib/api';
|
import { del, post } from '$lib/api';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
@ -14,23 +12,36 @@
|
|||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
async function saveStorage(newStorage = false) {
|
async function saveStorage(e: any) {
|
||||||
try {
|
try {
|
||||||
if (!storage.path) return errorNotification($t('application.storage.path_is_required'));
|
const formData = new FormData(e.target);
|
||||||
storage.path = storage.path.startsWith('/') ? storage.path : `/${storage.path}`;
|
let isNewStorage = true;
|
||||||
storage.path = storage.path.endsWith('/') ? storage.path.slice(0, -1) : storage.path;
|
let newStorage: any = {
|
||||||
storage.path.replace(/\/\//g, '/');
|
id: null,
|
||||||
|
containerId: null,
|
||||||
|
path: null
|
||||||
|
};
|
||||||
|
for (let field of formData) {
|
||||||
|
const [key, value] = field;
|
||||||
|
newStorage[key] = value;
|
||||||
|
}
|
||||||
|
newStorage.path = newStorage.path.startsWith('/') ? newStorage.path : `/${newStorage.path}`;
|
||||||
|
newStorage.path = newStorage.path.endsWith('/')
|
||||||
|
? newStorage.path.slice(0, -1)
|
||||||
|
: newStorage.path;
|
||||||
|
newStorage.path.replace(/\/\//g, '/');
|
||||||
await post(`/services/${id}/storages`, {
|
await post(`/services/${id}/storages`, {
|
||||||
path: storage.path,
|
path: newStorage.path,
|
||||||
storageId: storage.id,
|
storageId: newStorage.id,
|
||||||
newStorage
|
containerId: newStorage.containerId,
|
||||||
|
isNewStorage
|
||||||
});
|
});
|
||||||
dispatch('refresh');
|
dispatch('refresh');
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
storage.path = null;
|
storage.path = null;
|
||||||
storage.id = null;
|
storage.id = null;
|
||||||
}
|
}
|
||||||
if (newStorage) {
|
if (isNewStorage) {
|
||||||
addToast({
|
addToast({
|
||||||
message: $t('application.storage.storage_saved'),
|
message: $t('application.storage.storage_saved'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
@ -45,7 +56,7 @@
|
|||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function removeStorage() {
|
async function removeStorage(path: string) {
|
||||||
try {
|
try {
|
||||||
await del(`/services/${id}/storages`, { path: storage.path });
|
await del(`/services/${id}/storages`, { path: storage.path });
|
||||||
dispatch('refresh');
|
dispatch('refresh');
|
||||||
@ -61,74 +72,89 @@
|
|||||||
|
|
||||||
<div class="w-full lg:px-0 px-4">
|
<div class="w-full lg:px-0 px-4">
|
||||||
{#if storage.predefined}
|
{#if storage.predefined}
|
||||||
<div class="grid grid-col-1 lg:grid-cols-2 pt-2">
|
<div class="grid grid-col-1 lg:grid-cols-2 pt-2 gap-2">
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
id={storage.volumeName}
|
id={storage.containerId}
|
||||||
|
disabled
|
||||||
|
readonly
|
||||||
|
class="w-full"
|
||||||
|
value={`${
|
||||||
|
services.find((s) => s.id === storage.containerId).name || storage.containerId
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id={storage.volumeName}
|
||||||
disabled
|
disabled
|
||||||
readonly
|
readonly
|
||||||
class="w-full"
|
class="w-full"
|
||||||
value={`${storage.volumeName}:${storage.path}`}
|
value={`${storage.volumeName}:${storage.path}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="lg:px-2">
|
|
||||||
<input
|
|
||||||
id={storage.containerId}
|
|
||||||
disabled
|
|
||||||
readonly
|
|
||||||
class="w-full"
|
|
||||||
value={`${storage.containerId}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else if isNew}
|
||||||
<div class="grid grid-col-1 lg:grid-cols-3 lg:space-x-4" class:pt-8={isNew}>
|
<form id="saveVolumesForm" on:submit|preventDefault={saveStorage}>
|
||||||
{#if storage.id}
|
<div class="grid grid-col-1 lg:grid-cols-2 lg:space-x-4 pt-8">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-row gap-2">
|
||||||
<input
|
<div class="flex flex-col">
|
||||||
disabled
|
<label for="name" class="pb-2 uppercase font-bold">Container</label>
|
||||||
readonly
|
<select
|
||||||
class="w-full"
|
form="saveVolumesForm"
|
||||||
value="{storage.id}{storage.path.replace(/\//gi, '-')}"
|
name="containerId"
|
||||||
/>
|
class="w-full lg:w-64 font-normal"
|
||||||
</div>
|
disabled={storage.predefined}
|
||||||
{/if}
|
readonly={storage.predefined}
|
||||||
<div class="flex flex-col">
|
bind:value={storage.containerId}
|
||||||
{#if isNew}
|
|
||||||
<label for="name" class="pb-2 uppercase font-bold">Path</label>
|
|
||||||
{/if}
|
|
||||||
<input
|
|
||||||
disabled={storage.predefined}
|
|
||||||
readonly={storage.predefined}
|
|
||||||
class="w-full lg:w-64"
|
|
||||||
bind:value={storage.path}
|
|
||||||
required
|
|
||||||
placeholder="eg: /sqlite.db"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class:pt-8={isNew} class:pt-2={!isNew}>
|
|
||||||
{#if isNew}
|
|
||||||
<div class="flex items-center justify-center w-full lg:w-64">
|
|
||||||
<button class="btn btn-sm btn-primary w-full" on:click={() => saveStorage(true)}
|
|
||||||
>{$t('forms.add')}</button
|
|
||||||
>
|
>
|
||||||
|
{#if services.length === 1}
|
||||||
|
{#if services[0].name}
|
||||||
|
<option selected value={services[0].id}>{services[0].name}</option>
|
||||||
|
{:else}
|
||||||
|
<option selected value={services[0]}>{services[0]}</option>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
{#each services as service}
|
||||||
|
{#if service.name}
|
||||||
|
<option value={service.id}>{service.name}</option>
|
||||||
|
{:else}
|
||||||
|
<option value={service}>{service}</option>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
<div class="flex flex-col">
|
||||||
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-64">
|
<label for="name" class="pb-2 uppercase font-bold">Path</label>
|
||||||
<div class="flex items-center justify-center">
|
<input
|
||||||
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(false)}
|
name="path"
|
||||||
>{$t('forms.set')}</button
|
disabled={storage.predefined}
|
||||||
>
|
readonly={storage.predefined}
|
||||||
</div>
|
class="w-full lg:w-64"
|
||||||
<div class="flex justify-center">
|
bind:value={storage.path}
|
||||||
<button class="btn btn-sm btn-error" on:click={removeStorage}
|
required
|
||||||
>{$t('forms.remove')}</button
|
placeholder="eg: /sqlite.db"
|
||||||
>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-8">
|
||||||
|
<div class="flex items-center justify-center w-full lg:w-64">
|
||||||
|
<button type="submit" class="btn btn-sm btn-primary w-full">{$t('forms.add')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
{:else}
|
||||||
|
<div class="flex lg:flex-row flex-col items-center gap-2 py-1">
|
||||||
|
<input disabled readonly class="w-full" value={`${storage.containerId}`} />
|
||||||
|
<input disabled readonly class="w-full" value={`${storage.volumeName}:${storage.path}`} />
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-error"
|
||||||
|
on:click|stopPropagation|preventDefault={() => removeStorage(storage.path)}
|
||||||
|
>{$t('forms.remove')}</button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,9 +37,6 @@
|
|||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
let recommendedVersion = $appSession.supportedServiceTypesAndVersions.find(
|
|
||||||
({ name }) => name === type
|
|
||||||
)?.recommendedVersion;
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (versions.length === 1) {
|
if (versions.length === 1) {
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
// exposePort: service.exposePort
|
// exposePort: service.exposePort
|
||||||
// });
|
// });
|
||||||
const formData = new FormData(e.target);
|
const formData = new FormData(e.target);
|
||||||
service = await saveForm(formData, service);
|
if (formData) service = await saveForm(formData, service);
|
||||||
setLocation(service);
|
setLocation(service);
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
const response = await get(`/services/${params.id}/storages`);
|
const response = await get(`/services/${params.id}/storages`);
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
template: stuff.template,
|
||||||
...response
|
...response
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -19,6 +20,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let persistentStorages: any;
|
export let persistentStorages: any;
|
||||||
|
export let template: any;
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import Storage from './_Storage.svelte';
|
import Storage from './_Storage.svelte';
|
||||||
import { get } from '$lib/api';
|
import { get } from '$lib/api';
|
||||||
@ -30,6 +32,16 @@
|
|||||||
const data = await get(`/services/${id}/storages`);
|
const data = await get(`/services/${id}/storages`);
|
||||||
persistentStorages = [...data.persistentStorages];
|
persistentStorages = [...data.persistentStorages];
|
||||||
}
|
}
|
||||||
|
let services = Object.keys(template).map((service) => {
|
||||||
|
if (template[service]?.name) {
|
||||||
|
return {
|
||||||
|
name: template[service].name,
|
||||||
|
id: service
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@ -43,19 +55,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if persistentStorages.filter((s) => s.predefined).length > 0}
|
{#if persistentStorages.filter((s) => s.predefined).length > 0}
|
||||||
<div class="text-base font-bold">Predefined</div>
|
<div class="title">Predefined Volumes</div>
|
||||||
|
<div class="w-full lg:px-0 px-4">
|
||||||
|
<div class="grid grid-col-1 lg:grid-cols-2 pt-2 gap-2">
|
||||||
|
<div class="font-bold uppercase">Container</div>
|
||||||
|
<div class="font-bold uppercase">Volume ID : Mount Dir</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each persistentStorages.filter((s) => s.predefined) as storage}
|
{#each persistentStorages.filter((s) => s.predefined) as storage}
|
||||||
{#key storage.id}
|
{#key storage.id}
|
||||||
<Storage on:refresh={refreshStorage} {storage} />
|
<Storage on:refresh={refreshStorage} {storage} {services} />
|
||||||
{/key}
|
{/key}
|
||||||
{/each}
|
{/each}
|
||||||
<div class="text-base font-bold" class:pt-10={persistentStorages.filter((s) => s.predefined).length > 0}>User Defined</div>
|
<div class="title" class:pt-10={persistentStorages.filter((s) => s.predefined).length > 0}>
|
||||||
|
Custom Volumes
|
||||||
|
</div>
|
||||||
{#each persistentStorages.filter((s) => !s.predefined) as storage}
|
{#each persistentStorages.filter((s) => !s.predefined) as storage}
|
||||||
{#key storage.id}
|
{#key storage.id}
|
||||||
<Storage on:refresh={refreshStorage} {storage} />
|
<Storage on:refresh={refreshStorage} {storage} />
|
||||||
{/key}
|
{/key}
|
||||||
{/each}
|
{/each}
|
||||||
<Storage on:refresh={refreshStorage} isNew />
|
<Storage on:refresh={refreshStorage} isNew {services} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user