Merged v2.4.0
This commit is contained in:
commit
9da08d600b
@ -3,3 +3,4 @@ COOLIFY_SECRET_KEY=12341234123412341234123412341234
|
|||||||
COOLIFY_DATABASE_URL=file:../db/dev.db
|
COOLIFY_DATABASE_URL=file:../db/dev.db
|
||||||
COOLIFY_SENTRY_DSN=
|
COOLIFY_SENTRY_DSN=
|
||||||
COOLIFY_IS_ON="docker"
|
COOLIFY_IS_ON="docker"
|
||||||
|
COOLIFY_WHITE_LABELED="false"
|
@ -11,7 +11,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
LABEL coolify.managed true
|
LABEL coolify.managed true
|
||||||
|
|
||||||
RUN apk add --no-cache git git-lfs openssh-client curl jq cmake sqlite
|
RUN apk add --no-cache git git-lfs openssh-client curl jq cmake sqlite openssl
|
||||||
|
|
||||||
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6
|
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6
|
||||||
RUN pnpm add -g pnpm
|
RUN pnpm add -g pnpm
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||||
"version": "2.3.3",
|
"version": "2.4.0",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",
|
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Wordpress" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"extraConfig" TEXT,
|
||||||
|
"tablePrefix" TEXT,
|
||||||
|
"mysqlUser" TEXT NOT NULL,
|
||||||
|
"mysqlPassword" TEXT NOT NULL,
|
||||||
|
"mysqlRootUser" TEXT NOT NULL,
|
||||||
|
"mysqlRootUserPassword" TEXT NOT NULL,
|
||||||
|
"mysqlDatabase" TEXT,
|
||||||
|
"mysqlPublicPort" INTEGER,
|
||||||
|
"ftpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"ftpUser" TEXT,
|
||||||
|
"ftpPassword" TEXT,
|
||||||
|
"ftpPublicPort" INTEGER,
|
||||||
|
"ftpHostKey" TEXT,
|
||||||
|
"ftpHostKeyPrivate" TEXT,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress";
|
||||||
|
DROP TABLE "Wordpress";
|
||||||
|
ALTER TABLE "new_Wordpress" RENAME TO "Wordpress";
|
||||||
|
CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@ -332,6 +332,12 @@ model Wordpress {
|
|||||||
mysqlRootUserPassword String
|
mysqlRootUserPassword String
|
||||||
mysqlDatabase String?
|
mysqlDatabase String?
|
||||||
mysqlPublicPort Int?
|
mysqlPublicPort Int?
|
||||||
|
ftpEnabled Boolean @default(false)
|
||||||
|
ftpUser String?
|
||||||
|
ftpPassword String?
|
||||||
|
ftpPublicPort Int?
|
||||||
|
ftpHostKey String?
|
||||||
|
ftpHostKeyPrivate String?
|
||||||
serviceId String @unique
|
serviceId String @unique
|
||||||
service Service @relation(fields: [serviceId], references: [id])
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
3
src/app.d.ts
vendored
3
src/app.d.ts
vendored
@ -19,14 +19,13 @@ declare namespace App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SessionData {
|
interface SessionData {
|
||||||
|
whiteLabeled: boolean;
|
||||||
version?: string;
|
version?: string;
|
||||||
userId?: string | null;
|
userId?: string | null;
|
||||||
teamId?: string | null;
|
teamId?: string | null;
|
||||||
permission?: string;
|
permission?: string;
|
||||||
isAdmin?: boolean;
|
isAdmin?: boolean;
|
||||||
expires?: string | null;
|
expires?: string | null;
|
||||||
gitlabToken?: string | null;
|
|
||||||
ghToken?: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DateTimeFormatOptions = {
|
type DateTimeFormatOptions = {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Coolify</title>
|
<title>Coolify</title>
|
||||||
%svelte.head%
|
%svelte.head%
|
||||||
|
@ -7,6 +7,8 @@ import { version } from '$lib/common';
|
|||||||
import cookie from 'cookie';
|
import cookie from 'cookie';
|
||||||
import { dev } from '$app/env';
|
import { dev } from '$app/env';
|
||||||
|
|
||||||
|
const whiteLabeled = process.env['COOLIFY_WHITE_LABELED'] === 'true';
|
||||||
|
|
||||||
export const handle = handleSession(
|
export const handle = handleSession(
|
||||||
{
|
{
|
||||||
secret: process.env['COOLIFY_SECRET_KEY'],
|
secret: process.env['COOLIFY_SECRET_KEY'],
|
||||||
@ -71,6 +73,7 @@ export const handle = handleSession(
|
|||||||
export const getSession: GetSession = function ({ locals }) {
|
export const getSession: GetSession = function ({ locals }) {
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
|
whiteLabeled,
|
||||||
...locals.session.data
|
...locals.session.data
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,12 @@ import { promises as fs } from 'fs';
|
|||||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||||
const { workdir, baseDirectory } = data;
|
const { workdir, baseDirectory } = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
let composerFound = false;
|
||||||
|
try {
|
||||||
|
await fs.readFile(`${workdir}${baseDirectory || ''}/composer.json`);
|
||||||
|
composerFound = true;
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
Dockerfile.push(`LABEL coolify.image=true`);
|
Dockerfile.push(`LABEL coolify.image=true`);
|
||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
@ -11,6 +17,10 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
|||||||
if (htaccessFound) {
|
if (htaccessFound) {
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''}/.htaccess ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''}/.htaccess ./`);
|
||||||
}
|
}
|
||||||
|
if (composerFound) {
|
||||||
|
Dockerfile.push(`RUN composer install`);
|
||||||
|
}
|
||||||
|
|
||||||
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
|
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
|
||||||
Dockerfile.push(`EXPOSE 80`);
|
Dockerfile.push(`EXPOSE 80`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
@ -21,12 +31,14 @@ export default async function (data) {
|
|||||||
try {
|
try {
|
||||||
let htaccessFound = false;
|
let htaccessFound = false;
|
||||||
try {
|
try {
|
||||||
const d = await fs.readFile(`${workdir}${baseDirectory || ''}/.htaccess`);
|
await fs.readFile(`${workdir}${baseDirectory || ''}/.htaccess`);
|
||||||
htaccessFound = true;
|
htaccessFound = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
const image = htaccessFound ? 'webdevops/php-apache' : 'webdevops/php-nginx';
|
const image = htaccessFound
|
||||||
|
? 'webdevops/php-apache:8.0-alpine'
|
||||||
|
: 'webdevops/php-nginx:8.0-alpine';
|
||||||
await createDockerfile(data, image, htaccessFound);
|
await createDockerfile(data, image, htaccessFound);
|
||||||
await buildImage(data);
|
await buildImage(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
export let isCenter = true;
|
export let isCenter = true;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let dataTooltip = null;
|
export let dataTooltip = null;
|
||||||
|
export let loading = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex items-center py-4 pr-8">
|
<div class="flex items-center py-4 pr-8">
|
||||||
@ -26,9 +27,10 @@
|
|||||||
on:click
|
on:click
|
||||||
aria-pressed="false"
|
aria-pressed="false"
|
||||||
class="relative mx-20 inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
class="relative mx-20 inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
||||||
class:opacity-50={disabled}
|
class:opacity-50={disabled || loading}
|
||||||
class:bg-green-600={setting}
|
class:bg-green-600={!loading && setting}
|
||||||
class:bg-stone-700={!setting}
|
class:bg-stone-700={!loading && !setting}
|
||||||
|
class:bg-yellow-500={loading}
|
||||||
>
|
>
|
||||||
<span class="sr-only">Use setting</span>
|
<span class="sr-only">Use setting</span>
|
||||||
<span
|
<span
|
||||||
@ -40,6 +42,7 @@
|
|||||||
class=" absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-in"
|
class=" absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-in"
|
||||||
class:opacity-0={setting}
|
class:opacity-0={setting}
|
||||||
class:opacity-100={!setting}
|
class:opacity-100={!setting}
|
||||||
|
class:animate-spin={loading}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<svg class="h-3 w-3 bg-white text-red-600" fill="none" viewBox="0 0 12 12">
|
<svg class="h-3 w-3 bg-white text-red-600" fill="none" viewBox="0 0 12 12">
|
||||||
@ -57,6 +60,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class:opacity-100={setting}
|
class:opacity-100={setting}
|
||||||
class:opacity-0={!setting}
|
class:opacity-0={!setting}
|
||||||
|
class:animate-spin={loading}
|
||||||
>
|
>
|
||||||
<svg class="h-3 w-3 bg-white text-green-600" fill="currentColor" viewBox="0 0 12 12">
|
<svg class="h-3 w-3 bg-white text-green-600" fill="currentColor" viewBox="0 0 12 12">
|
||||||
<path
|
<path
|
||||||
|
@ -43,3 +43,142 @@ export function changeQueryParams(buildId) {
|
|||||||
queryParams.set('buildId', buildId);
|
queryParams.set('buildId', buildId);
|
||||||
return history.pushState(null, null, '?' + queryParams.toString());
|
return history.pushState(null, null, '?' + queryParams.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const supportedDatabaseTypesAndVersions = [
|
||||||
|
{
|
||||||
|
name: 'mongodb',
|
||||||
|
fancyName: 'MongoDB',
|
||||||
|
baseImage: 'bitnami/mongodb',
|
||||||
|
versions: ['5.0', '4.4', '4.2']
|
||||||
|
},
|
||||||
|
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
|
||||||
|
{
|
||||||
|
name: 'postgresql',
|
||||||
|
fancyName: 'PostgreSQL',
|
||||||
|
baseImage: 'bitnami/postgresql',
|
||||||
|
versions: ['14.2', '13.6', '12.10', '11.15', '10.20']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'redis',
|
||||||
|
fancyName: 'Redis',
|
||||||
|
baseImage: 'bitnami/redis',
|
||||||
|
versions: ['6.2', '6.0', '5.0']
|
||||||
|
},
|
||||||
|
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
|
||||||
|
];
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nocodb',
|
||||||
|
fancyName: 'NocoDB',
|
||||||
|
baseImage: 'nocodb/nocodb',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'minio',
|
||||||
|
fancyName: 'MinIO',
|
||||||
|
baseImage: 'minio/minio',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 9001
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vscodeserver',
|
||||||
|
fancyName: 'VSCode Server',
|
||||||
|
baseImage: 'codercom/code-server',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vaultwarden',
|
||||||
|
fancyName: 'Vaultwarden',
|
||||||
|
baseImage: 'vaultwarden/server',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'languagetool',
|
||||||
|
fancyName: 'LanguageTool',
|
||||||
|
baseImage: 'silviof/docker-languagetool',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8010
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'n8n',
|
||||||
|
fancyName: 'n8n',
|
||||||
|
baseImage: 'n8nio/n8n',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 5678
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'uptimekuma',
|
||||||
|
fancyName: 'Uptime Kuma',
|
||||||
|
baseImage: 'louislam/uptime-kuma',
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 3001
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ghost',
|
||||||
|
fancyName: 'Ghost',
|
||||||
|
baseImage: 'bitnami/ghost',
|
||||||
|
images: ['bitnami/mariadb'],
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 2368
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'meilisearch',
|
||||||
|
fancyName: 'Meilisearch',
|
||||||
|
baseImage: 'getmeili/meilisearch',
|
||||||
|
images: [],
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 7700
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
@ -14,7 +14,13 @@ import type {
|
|||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
|
|
||||||
export async function listApplications(teamId: string): Promise<Application[]> {
|
export async function listApplications(teamId: string): Promise<Application[]> {
|
||||||
return await prisma.application.findMany({ where: { teams: { some: { id: teamId } } } });
|
if (teamId === '0') {
|
||||||
|
return await prisma.application.findMany({ include: { teams: true } });
|
||||||
|
}
|
||||||
|
return await prisma.application.findMany({
|
||||||
|
where: { teams: { some: { id: teamId } } },
|
||||||
|
include: { teams: true }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function newApplication({
|
export async function newApplication({
|
||||||
@ -133,13 +139,7 @@ export async function getApplicationWebhook({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getApplication({
|
export async function getApplication({ id, teamId }: { id: string; teamId: string }): Promise<
|
||||||
id,
|
|
||||||
teamId
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
teamId: string;
|
|
||||||
}): Promise<
|
|
||||||
Application & {
|
Application & {
|
||||||
destinationDocker: DestinationDocker;
|
destinationDocker: DestinationDocker;
|
||||||
settings: ApplicationSettings;
|
settings: ApplicationSettings;
|
||||||
@ -148,7 +148,20 @@ export async function getApplication({
|
|||||||
persistentStorage: ApplicationPersistentStorage[];
|
persistentStorage: ApplicationPersistentStorage[];
|
||||||
}
|
}
|
||||||
> {
|
> {
|
||||||
const body = await prisma.application.findFirst({
|
let body;
|
||||||
|
if (teamId === '0') {
|
||||||
|
body = await prisma.application.findFirst({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
destinationDocker: true,
|
||||||
|
settings: true,
|
||||||
|
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
||||||
|
secrets: true,
|
||||||
|
persistentStorage: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
body = await prisma.application.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } },
|
where: { id, teams: { some: { id: teamId } } },
|
||||||
include: {
|
include: {
|
||||||
destinationDocker: true,
|
destinationDocker: true,
|
||||||
@ -158,6 +171,7 @@ export async function getApplication({
|
|||||||
persistentStorage: true
|
persistentStorage: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (body?.gitSource?.githubApp?.clientSecret) {
|
if (body?.gitSource?.githubApp?.clientSecret) {
|
||||||
body.gitSource.githubApp.clientSecret = decrypt(body.gitSource.githubApp.clientSecret);
|
body.gitSource.githubApp.clientSecret = decrypt(body.gitSource.githubApp.clientSecret);
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { dev } from '$app/env';
|
import { dev } from '$app/env';
|
||||||
import { sentry } from '$lib/common';
|
import { sentry } from '$lib/common';
|
||||||
|
import {
|
||||||
|
supportedDatabaseTypesAndVersions,
|
||||||
|
supportedServiceTypesAndVersions
|
||||||
|
} from '$lib/components/common';
|
||||||
import * as Prisma from '@prisma/client';
|
import * as Prisma from '@prisma/client';
|
||||||
import { default as ProdPrisma } from '@prisma/client';
|
import { default as ProdPrisma } from '@prisma/client';
|
||||||
import type { Database, DatabaseSettings } from '@prisma/client';
|
import type { Database, DatabaseSettings } from '@prisma/client';
|
||||||
@ -87,134 +91,6 @@ export async function generateSshKeyPair(): Promise<{ publicKey: string; private
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const supportedDatabaseTypesAndVersions = [
|
|
||||||
{
|
|
||||||
name: 'mongodb',
|
|
||||||
fancyName: 'MongoDB',
|
|
||||||
baseImage: 'bitnami/mongodb',
|
|
||||||
versions: ['5.0.5', '4.4.11', '4.2.18', '4.0.27']
|
|
||||||
},
|
|
||||||
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0.27', '5.7.36'] },
|
|
||||||
{
|
|
||||||
name: 'postgresql',
|
|
||||||
fancyName: 'PostgreSQL',
|
|
||||||
baseImage: 'bitnami/postgresql',
|
|
||||||
versions: ['14.1.0', '13.5.0', '12.9.0', '11.14.0', '10.19.0', '9.6.24']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'redis',
|
|
||||||
fancyName: 'Redis',
|
|
||||||
baseImage: 'bitnami/redis',
|
|
||||||
versions: ['6.2.6', '6.0.16', '5.0.14']
|
|
||||||
},
|
|
||||||
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
|
|
||||||
];
|
|
||||||
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'],
|
|
||||||
ports: {
|
|
||||||
main: 8000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nocodb',
|
|
||||||
fancyName: 'NocoDB',
|
|
||||||
baseImage: 'nocodb/nocodb',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'minio',
|
|
||||||
fancyName: 'MinIO',
|
|
||||||
baseImage: 'minio/minio',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 9001
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vscodeserver',
|
|
||||||
fancyName: 'VSCode Server',
|
|
||||||
baseImage: 'codercom/code-server',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wordpress',
|
|
||||||
fancyName: 'Wordpress',
|
|
||||||
baseImage: 'wordpress',
|
|
||||||
images: ['bitnami/mysql:5.7'],
|
|
||||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vaultwarden',
|
|
||||||
fancyName: 'Vaultwarden',
|
|
||||||
baseImage: 'vaultwarden/server',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languagetool',
|
|
||||||
fancyName: 'LanguageTool',
|
|
||||||
baseImage: 'silviof/docker-languagetool',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 8010
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'n8n',
|
|
||||||
fancyName: 'n8n',
|
|
||||||
baseImage: 'n8nio/n8n',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 5678
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'uptimekuma',
|
|
||||||
fancyName: 'Uptime Kuma',
|
|
||||||
baseImage: 'louislam/uptime-kuma',
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 3001
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ghost',
|
|
||||||
fancyName: 'Ghost',
|
|
||||||
baseImage: 'bitnami/ghost',
|
|
||||||
images: ['bitnami/mariadb'],
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 2368
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'meilisearch',
|
|
||||||
fancyName: 'Meilisearch',
|
|
||||||
baseImage: 'getmeili/meilisearch',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
ports: {
|
|
||||||
main: 7700
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getVersions(type: string): string[] {
|
export function getVersions(type: string): string[] {
|
||||||
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
|
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
|
||||||
if (found) {
|
if (found) {
|
||||||
|
@ -6,7 +6,14 @@ import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
|
|||||||
import type { Database, DatabaseSettings, DestinationDocker } from '@prisma/client';
|
import type { Database, DatabaseSettings, DestinationDocker } from '@prisma/client';
|
||||||
|
|
||||||
export async function listDatabases(teamId: string): Promise<Database[]> {
|
export async function listDatabases(teamId: string): Promise<Database[]> {
|
||||||
return await prisma.database.findMany({ where: { teams: { some: { id: teamId } } } });
|
if (teamId === '0') {
|
||||||
|
return await prisma.database.findMany({ include: { teams: true } });
|
||||||
|
} else {
|
||||||
|
return await prisma.database.findMany({
|
||||||
|
where: { teams: { some: { id: teamId } } },
|
||||||
|
include: { teams: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function newDatabase({
|
export async function newDatabase({
|
||||||
@ -43,11 +50,18 @@ export async function getDatabase({
|
|||||||
id: string;
|
id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
}): Promise<Database & { destinationDocker: DestinationDocker; settings: DatabaseSettings }> {
|
}): Promise<Database & { destinationDocker: DestinationDocker; settings: DatabaseSettings }> {
|
||||||
const body = await prisma.database.findFirst({
|
let body;
|
||||||
|
if (teamId === '0') {
|
||||||
|
body = await prisma.database.findFirst({
|
||||||
|
where: { id },
|
||||||
|
include: { destinationDocker: true, settings: true }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
body = await prisma.database.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } },
|
where: { id, teams: { some: { id: teamId } } },
|
||||||
include: { destinationDocker: true, settings: true }
|
include: { destinationDocker: true, settings: true }
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (body.dbUserPassword) body.dbUserPassword = decrypt(body.dbUserPassword);
|
if (body.dbUserPassword) body.dbUserPassword = decrypt(body.dbUserPassword);
|
||||||
if (body.rootUserPassword) body.rootUserPassword = decrypt(body.rootUserPassword);
|
if (body.rootUserPassword) body.rootUserPassword = decrypt(body.rootUserPassword);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { asyncExecShell, getEngine } from '$lib/common';
|
import { asyncExecShell, getEngine } from '$lib/common';
|
||||||
import { decrypt } from '$lib/crypto';
|
|
||||||
import { dockerInstance } from '$lib/docker';
|
import { dockerInstance } from '$lib/docker';
|
||||||
import { startCoolifyProxy } from '$lib/haproxy';
|
import { startCoolifyProxy } from '$lib/haproxy';
|
||||||
import { getDatabaseImage } from '.';
|
import { getDatabaseImage } from '.';
|
||||||
@ -18,7 +17,13 @@ type FindDestinationFromTeam = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function listDestinations(teamId: string): Promise<DestinationDocker[]> {
|
export async function listDestinations(teamId: string): Promise<DestinationDocker[]> {
|
||||||
return await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId } } } });
|
if (teamId === '0') {
|
||||||
|
return await prisma.destinationDocker.findMany({ include: { teams: true } });
|
||||||
|
}
|
||||||
|
return await prisma.destinationDocker.findMany({
|
||||||
|
where: { teams: { some: { id: teamId } } },
|
||||||
|
include: { teams: true }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function configureDestinationForService({
|
export async function configureDestinationForService({
|
||||||
@ -146,12 +151,17 @@ export async function getDestination({
|
|||||||
id,
|
id,
|
||||||
teamId
|
teamId
|
||||||
}: FindDestinationFromTeam): Promise<DestinationDocker & { sshPrivateKey?: string }> {
|
}: FindDestinationFromTeam): Promise<DestinationDocker & { sshPrivateKey?: string }> {
|
||||||
const destination = (await prisma.destinationDocker.findFirst({
|
let destination;
|
||||||
|
if (teamId === '0') {
|
||||||
|
destination = await prisma.destinationDocker.findFirst({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
destination = await prisma.destinationDocker.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } }
|
where: { id, teams: { some: { id: teamId } } }
|
||||||
})) as DestinationDocker & { sshPrivateKey?: string };
|
});
|
||||||
if (destination.remoteEngine) {
|
|
||||||
destination.sshPrivateKey = decrypt(destination.sshPrivateKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
export async function getDestinationByApplicationId({
|
export async function getDestinationByApplicationId({
|
||||||
|
@ -5,9 +5,14 @@ import type { GithubApp, GitlabApp, GitSource, Prisma, Application } from '@pris
|
|||||||
export async function listSources(
|
export async function listSources(
|
||||||
teamId: string | Prisma.StringFilter
|
teamId: string | Prisma.StringFilter
|
||||||
): Promise<(GitSource & { githubApp?: GithubApp; gitlabApp?: GitlabApp })[]> {
|
): Promise<(GitSource & { githubApp?: GithubApp; gitlabApp?: GitlabApp })[]> {
|
||||||
|
if (teamId === '0') {
|
||||||
|
return await prisma.gitSource.findMany({
|
||||||
|
include: { githubApp: true, gitlabApp: true, teams: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
return await prisma.gitSource.findMany({
|
return await prisma.gitSource.findMany({
|
||||||
where: { teams: { some: { id: teamId } } },
|
where: { teams: { some: { id: teamId } } },
|
||||||
include: { githubApp: true, gitlabApp: true }
|
include: { githubApp: true, gitlabApp: true, teams: true }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,10 +59,18 @@ export async function getSource({
|
|||||||
id: string;
|
id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
}): Promise<GitSource & { githubApp: GithubApp; gitlabApp: GitlabApp }> {
|
}): Promise<GitSource & { githubApp: GithubApp; gitlabApp: GitlabApp }> {
|
||||||
const body = await prisma.gitSource.findFirst({
|
let body;
|
||||||
|
if (teamId === '0') {
|
||||||
|
body = await prisma.gitSource.findFirst({
|
||||||
|
where: { id },
|
||||||
|
include: { githubApp: true, gitlabApp: true }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
body = await prisma.gitSource.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } },
|
where: { id, teams: { some: { id: teamId } } },
|
||||||
include: { githubApp: true, gitlabApp: true }
|
include: { githubApp: true, gitlabApp: true }
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (body?.githubApp?.clientSecret)
|
if (body?.githubApp?.clientSecret)
|
||||||
body.githubApp.clientSecret = decrypt(body.githubApp.clientSecret);
|
body.githubApp.clientSecret = decrypt(body.githubApp.clientSecret);
|
||||||
if (body?.githubApp?.webhookSecret)
|
if (body?.githubApp?.webhookSecret)
|
||||||
|
@ -5,7 +5,14 @@ import { generatePassword } from '.';
|
|||||||
import { prisma } from './common';
|
import { prisma } from './common';
|
||||||
|
|
||||||
export async function listServices(teamId: string): Promise<Service[]> {
|
export async function listServices(teamId: string): Promise<Service[]> {
|
||||||
return await prisma.service.findMany({ where: { teams: { some: { id: teamId } } } });
|
if (teamId === '0') {
|
||||||
|
return await prisma.service.findMany({ include: { teams: true } });
|
||||||
|
} else {
|
||||||
|
return await prisma.service.findMany({
|
||||||
|
where: { teams: { some: { id: teamId } } },
|
||||||
|
include: { teams: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function newService({
|
export async function newService({
|
||||||
@ -19,9 +26,8 @@ export async function newService({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getService({ id, teamId }: { id: string; teamId: string }): Promise<Service> {
|
export async function getService({ id, teamId }: { id: string; teamId: string }): Promise<Service> {
|
||||||
const body = await prisma.service.findFirst({
|
let body;
|
||||||
where: { id, teams: { some: { id: teamId } } },
|
const include = {
|
||||||
include: {
|
|
||||||
destinationDocker: true,
|
destinationDocker: true,
|
||||||
plausibleAnalytics: true,
|
plausibleAnalytics: true,
|
||||||
minio: true,
|
minio: true,
|
||||||
@ -30,8 +36,18 @@ export async function getService({ id, teamId }: { id: string; teamId: string })
|
|||||||
ghost: true,
|
ghost: true,
|
||||||
serviceSecret: true,
|
serviceSecret: true,
|
||||||
meiliSearch: true
|
meiliSearch: true
|
||||||
}
|
};
|
||||||
|
if (teamId === '0') {
|
||||||
|
body = await prisma.service.findFirst({
|
||||||
|
where: { id },
|
||||||
|
include
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
body = await prisma.service.findFirst({
|
||||||
|
where: { id, teams: { some: { id: teamId } } },
|
||||||
|
include
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (body.plausibleAnalytics?.postgresqlPassword)
|
if (body.plausibleAnalytics?.postgresqlPassword)
|
||||||
body.plausibleAnalytics.postgresqlPassword = decrypt(
|
body.plausibleAnalytics.postgresqlPassword = decrypt(
|
||||||
@ -65,8 +81,12 @@ export async function getService({ id, teamId }: { id: string; teamId: string })
|
|||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (body.wordpress?.ftpPassword) {
|
||||||
|
body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
|
||||||
|
}
|
||||||
|
const settings = await prisma.setting.findFirst();
|
||||||
|
|
||||||
return { ...body };
|
return { ...body, settings };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function configureServiceType({
|
export async function configureServiceType({
|
||||||
@ -191,6 +211,7 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setServiceVersion({
|
export async function setServiceVersion({
|
||||||
id,
|
id,
|
||||||
version
|
version
|
||||||
@ -233,6 +254,7 @@ export async function updatePlausibleAnalyticsService({
|
|||||||
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
|
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
|
||||||
await prisma.service.update({ where: { id }, data: { name, fqdn } });
|
await prisma.service.update({ where: { id }, data: { name, fqdn } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateService({
|
export async function updateService({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -244,6 +266,7 @@ export async function updateService({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateLanguageToolService({
|
export async function updateLanguageToolService({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -255,6 +278,7 @@ export async function updateLanguageToolService({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateMeiliSearchService({
|
export async function updateMeiliSearchService({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -266,6 +290,7 @@ export async function updateMeiliSearchService({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateVaultWardenService({
|
export async function updateVaultWardenService({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -277,6 +302,7 @@ export async function updateVaultWardenService({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateVsCodeServer({
|
export async function updateVsCodeServer({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -288,6 +314,7 @@ export async function updateVsCodeServer({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateWordpress({
|
export async function updateWordpress({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
@ -306,6 +333,7 @@ export async function updateWordpress({
|
|||||||
data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } }
|
data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateMinioService({
|
export async function updateMinioService({
|
||||||
id,
|
id,
|
||||||
publicPort
|
publicPort
|
||||||
@ -315,6 +343,7 @@ export async function updateMinioService({
|
|||||||
}): Promise<Minio> {
|
}): Promise<Minio> {
|
||||||
return await prisma.minio.update({ where: { serviceId: id }, data: { publicPort } });
|
return await prisma.minio.update({ where: { serviceId: id }, data: { publicPort } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateGhostService({
|
export async function updateGhostService({
|
||||||
id,
|
id,
|
||||||
fqdn,
|
fqdn,
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { dev } from '$app/env';
|
import { dev } from '$app/env';
|
||||||
import got, { type Got } from 'got';
|
import got, { type Got } from 'got';
|
||||||
import mustache from 'mustache';
|
|
||||||
import crypto from 'crypto';
|
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { checkContainer, checkHAProxy } from '.';
|
import { checkContainer, checkHAProxy } from '.';
|
||||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||||
|
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||||
|
|
||||||
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
|
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
|
||||||
|
|
||||||
@ -222,7 +221,7 @@ export async function configureHAProxy(): Promise<void> {
|
|||||||
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
const { engine } = destinationDocker;
|
const { engine } = destinationDocker;
|
||||||
const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type);
|
const found = supportedServiceTypesAndVersions.find((a) => a.name === 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;
|
||||||
@ -263,20 +262,36 @@ export async function configureHAProxy(): Promise<void> {
|
|||||||
redirectValue,
|
redirectValue,
|
||||||
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain
|
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain
|
||||||
});
|
});
|
||||||
}
|
for (const service of services) {
|
||||||
const output = mustache.render(template, data);
|
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
||||||
const newHash = crypto.createHash('md5').update(output).digest('hex');
|
if (destinationDockerId) {
|
||||||
const { proxyHash, id } = await db.listSettings();
|
const { engine } = destinationDocker;
|
||||||
if (proxyHash !== newHash) {
|
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||||
await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } });
|
if (found) {
|
||||||
await haproxy.post(`v2/services/haproxy/configuration/raw`, {
|
const port = found.ports.main;
|
||||||
searchParams: {
|
const publicPort = service[type]?.publicPort;
|
||||||
skip_version: true
|
const isRunning = await checkContainer(engine, id);
|
||||||
},
|
if (fqdn) {
|
||||||
body: output,
|
const domain = getDomain(fqdn);
|
||||||
headers: {
|
const isHttps = fqdn.startsWith('https://');
|
||||||
'Content-Type': 'text/plain'
|
const isWWW = fqdn.includes('www.');
|
||||||
}
|
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||||
|
if (isRunning) {
|
||||||
|
data.services.push({
|
||||||
|
id,
|
||||||
|
port,
|
||||||
|
publicPort,
|
||||||
|
domain,
|
||||||
|
isRunning,
|
||||||
|
isHttps,
|
||||||
|
redirectValue,
|
||||||
|
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
|
||||||
|
updatedAt: updatedAt.getTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -115,12 +115,12 @@ export async function stopTcpHttpProxy(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startTcpProxy(
|
export async function startTcpProxy(
|
||||||
destinationDocker: DestinationDocker,
|
destinationDocker: DestinationDocker,
|
||||||
id: string,
|
id: string,
|
||||||
publicPort: number,
|
publicPort: number,
|
||||||
privatePort: number
|
privatePort: number,
|
||||||
|
volume?: string
|
||||||
): Promise<{ stdout: string; stderr: string } | Error> {
|
): Promise<{ stdout: string; stderr: string } | Error> {
|
||||||
const { network, engine } = destinationDocker;
|
const { network, engine } = destinationDocker;
|
||||||
const host = getEngine(engine);
|
const host = getEngine(engine);
|
||||||
@ -136,7 +136,9 @@ export async function startTcpProxy(
|
|||||||
);
|
);
|
||||||
const ip = JSON.parse(Config)[0].Gateway;
|
const ip = JSON.parse(Config)[0].Gateway;
|
||||||
return await asyncExecShell(
|
return await asyncExecShell(
|
||||||
`DOCKER_HOST=${host} docker run --restart always -e PORT=${publicPort} -e APP=${id} -e PRIVATE_PORT=${privatePort} --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} -d coollabsio/${defaultProxyImageTcp}`
|
`DOCKER_HOST=${host} docker run --restart always -e PORT=${publicPort} -e APP=${id} -e PRIVATE_PORT=${privatePort} --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} ${
|
||||||
|
volume ? `-v ${volume}` : ''
|
||||||
|
} -d coollabsio/${defaultProxyImageTcp}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -4,6 +4,7 @@ import * as db from '$lib/database';
|
|||||||
import { dev } from '$app/env';
|
import { dev } from '$app/env';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import getPort, { portNumbers } from 'get-port';
|
import getPort, { portNumbers } from 'get-port';
|
||||||
|
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||||
|
|
||||||
export async function letsEncrypt(domain: string, id?: string, isCoolify = false): Promise<void> {
|
export async function letsEncrypt(domain: string, id?: string, isCoolify = false): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@ -160,7 +161,7 @@ export async function generateSSLCerts(): Promise<void> {
|
|||||||
type,
|
type,
|
||||||
destinationDocker: { engine }
|
destinationDocker: { engine }
|
||||||
} = service;
|
} = service;
|
||||||
const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type);
|
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||||
if (found) {
|
if (found) {
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const isHttps = fqdn.startsWith('https://');
|
const isHttps = fqdn.startsWith('https://');
|
||||||
|
@ -23,11 +23,9 @@ import yaml from 'js-yaml';
|
|||||||
import type { Job } from 'bullmq';
|
import type { Job } from 'bullmq';
|
||||||
import type { BuilderJob } from '$lib/types/builderJob';
|
import type { BuilderJob } from '$lib/types/builderJob';
|
||||||
|
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export default async function (job: Job<BuilderJob, void, string>): Promise<void> {
|
export default async function (job: Job<BuilderJob, void, string>): Promise<void> {
|
||||||
/*
|
|
||||||
Edge cases:
|
|
||||||
1 - Change build pack and redeploy, what should happen?
|
|
||||||
*/
|
|
||||||
const {
|
const {
|
||||||
id: applicationId,
|
id: applicationId,
|
||||||
repository,
|
repository,
|
||||||
@ -276,7 +274,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const compose = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[imageId]: {
|
[imageId]: {
|
||||||
@ -285,7 +283,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
|||||||
volumes,
|
volumes,
|
||||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||||
networks: [docker.network],
|
networks: [docker.network],
|
||||||
labels: labels,
|
labels,
|
||||||
depends_on: [],
|
depends_on: [],
|
||||||
restart: 'always'
|
restart: 'always'
|
||||||
}
|
}
|
||||||
@ -297,7 +295,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
|||||||
},
|
},
|
||||||
volumes: Object.assign({}, ...composeVolumes)
|
volumes: Object.assign({}, ...composeVolumes)
|
||||||
};
|
};
|
||||||
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(compose));
|
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`DOCKER_HOST=${host} docker compose --project-directory ${workdir} up -d`
|
`DOCKER_HOST=${host} docker compose --project-directory ${workdir} up -d`
|
||||||
);
|
);
|
||||||
|
53
src/lib/types/composeFile.ts
Normal file
53
src/lib/types/composeFile.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
export type ComposeFile = {
|
||||||
|
version: ComposerFileVersion;
|
||||||
|
services: Record<string, ComposeFileService>;
|
||||||
|
networks: Record<string, ComposeFileNetwork>;
|
||||||
|
volumes?: Record<string, ComposeFileVolume>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComposeFileService = {
|
||||||
|
container_name: string;
|
||||||
|
image?: string;
|
||||||
|
networks: string[];
|
||||||
|
environment?: Record<string, unknown>;
|
||||||
|
volumes?: string[];
|
||||||
|
ulimits?: unknown;
|
||||||
|
labels?: string[];
|
||||||
|
env_file?: string[];
|
||||||
|
extra_hosts?: string[];
|
||||||
|
restart: ComposeFileRestartOption;
|
||||||
|
depends_on?: string[];
|
||||||
|
command?: string;
|
||||||
|
build?: {
|
||||||
|
context: string;
|
||||||
|
dockerfile: string;
|
||||||
|
args?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComposerFileVersion =
|
||||||
|
| '3.8'
|
||||||
|
| '3.7'
|
||||||
|
| '3.6'
|
||||||
|
| '3.5'
|
||||||
|
| '3.4'
|
||||||
|
| '3.3'
|
||||||
|
| '3.2'
|
||||||
|
| '3.1'
|
||||||
|
| '3.0'
|
||||||
|
| '2.4'
|
||||||
|
| '2.3'
|
||||||
|
| '2.2'
|
||||||
|
| '2.1'
|
||||||
|
| '2.0';
|
||||||
|
|
||||||
|
export type ComposeFileRestartOption = 'no' | 'always' | 'on-failure' | 'unless-stopped';
|
||||||
|
|
||||||
|
export type ComposeFileNetwork = {
|
||||||
|
external: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComposeFileVolume = {
|
||||||
|
external?: boolean;
|
||||||
|
name?: string;
|
||||||
|
};
|
@ -134,13 +134,18 @@
|
|||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Coolify</title>
|
<title>Coolify</title>
|
||||||
|
{#if !$session.whiteLabeled}
|
||||||
|
<link rel="icon" href="/favicon.png" />
|
||||||
|
{/if}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
|
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
|
||||||
{#if $session.userId}
|
{#if $session.userId}
|
||||||
<nav class="nav-main">
|
<nav class="nav-main">
|
||||||
<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
|
<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
|
||||||
|
{#if !$session.whiteLabeled}
|
||||||
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
|
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
|
||||||
<div class="flex flex-col space-y-4 py-2">
|
{/if}
|
||||||
|
<div class="flex flex-col space-y-4 py-2" class:mt-2={$session.whiteLabeled}>
|
||||||
<a
|
<a
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
href="/"
|
href="/"
|
||||||
@ -312,7 +317,6 @@
|
|||||||
<path d="M7 18a4.6 4.4 0 0 1 0 -9a5 4.5 0 0 1 11 2h1a3.5 3.5 0 0 1 0 7h-12" />
|
<path d="M7 18a4.6 4.4 0 0 1 0 -9a5 4.5 0 0 1 11 2h1a3.5 3.5 0 0 1 0 7h-12" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<div class="border-t border-stone-700" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1" />
|
<div class="flex-1" />
|
||||||
|
|
||||||
@ -514,6 +518,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
{#if $session.whiteLabeled}
|
||||||
|
<span class="fixed bottom-0 left-[50px] z-50 m-2 px-4 text-xs text-stone-700"
|
||||||
|
>Powered by <a href="https://coolify.io" target="_blank">Coolify</a></span
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="fixed right-0 bottom-0 z-50 m-2 w-64 bg-opacity-30 p-2 px-4"
|
class="fixed right-0 bottom-0 z-50 m-2 w-64 bg-opacity-30 p-2 px-4"
|
||||||
bind:value={selectedTeamId}
|
bind:value={selectedTeamId}
|
||||||
|
@ -81,6 +81,9 @@
|
|||||||
);
|
);
|
||||||
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'blob');
|
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'blob');
|
||||||
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'blob');
|
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'blob');
|
||||||
|
const composerPHP = files.find(
|
||||||
|
(file) => file.name === 'composer.json' && file.type === 'blob'
|
||||||
|
);
|
||||||
|
|
||||||
if (yarnLock) packageManager = 'yarn';
|
if (yarnLock) packageManager = 'yarn';
|
||||||
if (pnpmLock) packageManager = 'pnpm';
|
if (pnpmLock) packageManager = 'pnpm';
|
||||||
@ -103,7 +106,7 @@
|
|||||||
foundConfig = findBuildPack('python');
|
foundConfig = findBuildPack('python');
|
||||||
} else if (indexHtml) {
|
} else if (indexHtml) {
|
||||||
foundConfig = findBuildPack('static', packageManager);
|
foundConfig = findBuildPack('static', packageManager);
|
||||||
} else if (indexPHP) {
|
} else if (indexPHP || composerPHP) {
|
||||||
foundConfig = findBuildPack('php');
|
foundConfig = findBuildPack('php');
|
||||||
} else {
|
} else {
|
||||||
foundConfig = findBuildPack('node', packageManager);
|
foundConfig = findBuildPack('node', packageManager);
|
||||||
@ -127,6 +130,9 @@
|
|||||||
);
|
);
|
||||||
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'file');
|
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'file');
|
||||||
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'file');
|
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'file');
|
||||||
|
const composerPHP = files.find(
|
||||||
|
(file) => file.name === 'composer.json' && file.type === 'file'
|
||||||
|
);
|
||||||
|
|
||||||
if (yarnLock) packageManager = 'yarn';
|
if (yarnLock) packageManager = 'yarn';
|
||||||
if (pnpmLock) packageManager = 'pnpm';
|
if (pnpmLock) packageManager = 'pnpm';
|
||||||
@ -146,7 +152,7 @@
|
|||||||
foundConfig = findBuildPack('python');
|
foundConfig = findBuildPack('python');
|
||||||
} else if (indexHtml) {
|
} else if (indexHtml) {
|
||||||
foundConfig = findBuildPack('static', packageManager);
|
foundConfig = findBuildPack('static', packageManager);
|
||||||
} else if (indexPHP) {
|
} else if (indexPHP || composerPHP) {
|
||||||
foundConfig = findBuildPack('php');
|
foundConfig = findBuildPack('php');
|
||||||
} else {
|
} else {
|
||||||
foundConfig = findBuildPack('node', packageManager);
|
foundConfig = findBuildPack('node', packageManager);
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
<div class="flex flex-col justify-center">
|
<div class="flex flex-col justify-center">
|
||||||
{#if !filteredSources || filteredSources.length === 0}
|
{#if !filteredSources || filteredSources.length === 0}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="pb-2">No configurable Git Source found</div>
|
<div class="pb-2 text-center">No configurable Git Source found</div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<a href="/new/source" sveltekit:prefetch class="add-icon bg-orange-600 hover:bg-orange-500">
|
<a href="/new/source" sveltekit:prefetch class="add-icon bg-orange-600 hover:bg-orange-500">
|
||||||
<svg
|
<svg
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
||||||
import Astro from '$lib/components/svg/applications/Astro.svelte';
|
import Astro from '$lib/components/svg/applications/Astro.svelte';
|
||||||
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
|
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
|
||||||
|
import { session } from '$app/stores';
|
||||||
|
|
||||||
const buildPack = application?.buildPack?.toLowerCase();
|
const buildPack = application?.buildPack?.toLowerCase();
|
||||||
</script>
|
</script>
|
||||||
@ -54,6 +55,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="truncate text-center text-xl font-bold">{application.name}</div>
|
<div class="truncate text-center text-xl font-bold">{application.name}</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
|
<div class="truncate text-center">Team {application.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
{#if application.fqdn}
|
{#if application.fqdn}
|
||||||
<div class="truncate text-center">{application.fqdn}</div>
|
<div class="truncate text-center">{application.fqdn}</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import { supportedDatabaseTypesAndVersions } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import { supportedDatabaseTypesAndVersions } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
|
@ -6,6 +6,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { makeLabelForStandaloneDatabase } from '$lib/buildPacks/common';
|
import { makeLabelForStandaloneDatabase } from '$lib/buildPacks/common';
|
||||||
import { startTcpProxy } from '$lib/haproxy';
|
import { startTcpProxy } from '$lib/haproxy';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -33,7 +34,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
|
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { session } from '$app/stores';
|
||||||
|
|
||||||
async function newDatabase() {
|
async function newDatabase() {
|
||||||
const { id } = await post('/databases/new', {});
|
const { id } = await post('/databases/new', {});
|
||||||
@ -59,6 +60,9 @@
|
|||||||
<div class="font-bold text-xl text-center truncate">
|
<div class="font-bold text-xl text-center truncate">
|
||||||
{database.name}
|
{database.name}
|
||||||
</div>
|
</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
|
<div class="text-center truncate">Team {database.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
{#if !database.type}
|
{#if !database.type}
|
||||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||||
Configuration missing
|
Configuration missing
|
||||||
|
@ -184,6 +184,7 @@
|
|||||||
value={destination.network}
|
value={destination.network}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
disabled={cannotDisable}
|
disabled={cannotDisable}
|
||||||
@ -197,28 +198,5 @@
|
|||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
<!-- <div class="flex justify-center">
|
|
||||||
{#if payload.isCoolifyProxyUsed}
|
|
||||||
{#if state}
|
|
||||||
<button on:click={stopProxy}>Stop proxy</button>
|
|
||||||
{:else}
|
|
||||||
<button on:click={startProxy}>Start proxy</button>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- {#if scannedApps.length > 0}
|
|
||||||
<div class="flex justify-center px-6 pb-10">
|
|
||||||
<div class="flex space-x-2 h-8 items-center">
|
|
||||||
<div class="font-bold text-xl text-white">Found applications</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="max-w-4xl mx-auto px-6">
|
|
||||||
<div class="flex space-x-2 justify-center">
|
|
||||||
{#each scannedApps as app}
|
|
||||||
<FoundApp {app} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if} -->
|
|
||||||
|
@ -8,7 +8,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
console.log(teamId);
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
try {
|
try {
|
||||||
const destination = await db.getDestination({ id, teamId });
|
const destination = await db.getDestination({ id, teamId });
|
||||||
|
@ -57,6 +57,9 @@
|
|||||||
<a href="/destinations/{destination.id}" class="no-underline p-2 w-96">
|
<a href="/destinations/{destination.id}" class="no-underline p-2 w-96">
|
||||||
<div class="box-selection hover:bg-sky-600">
|
<div class="box-selection hover:bg-sky-600">
|
||||||
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
|
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
|
<div class="text-center truncate">Team {destination.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
<div class="text-center truncate">{destination.network}</div>
|
<div class="text-center truncate">{destination.network}</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { session } from '$app/stores';
|
||||||
|
|
||||||
export let payload;
|
export let payload;
|
||||||
|
|
||||||
@ -56,6 +57,7 @@
|
|||||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||||
</div>
|
</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
bind:setting={payload.isCoolifyProxyUsed}
|
bind:setting={payload.isCoolifyProxyUsed}
|
||||||
@ -64,5 +66,6 @@
|
|||||||
description="This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).<br><br>Databases will have their own proxy."
|
description="This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).<br><br>Databases will have their own proxy."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
export let service;
|
export let service;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
export let readOnly;
|
export let readOnly;
|
||||||
|
export let settings;
|
||||||
|
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
@ -91,7 +92,22 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="buildPack" class="text-base font-bold text-stone-100">Version / Tag</label>
|
||||||
|
<a
|
||||||
|
href={$session.isAdmin
|
||||||
|
? `/services/${id}/configuration/version?from=/services/${id}`
|
||||||
|
: ''}
|
||||||
|
class="no-underline"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
value={service.version}
|
||||||
|
id="service"
|
||||||
|
disabled
|
||||||
|
class="cursor-pointer hover:bg-coolgray-500"
|
||||||
|
/></a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
||||||
<div>
|
<div>
|
||||||
@ -143,7 +159,7 @@
|
|||||||
{:else if service.type === 'vscodeserver'}
|
{:else if service.type === 'vscodeserver'}
|
||||||
<VsCodeServer {service} />
|
<VsCodeServer {service} />
|
||||||
{:else if service.type === 'wordpress'}
|
{:else if service.type === 'wordpress'}
|
||||||
<Wordpress bind:service {isRunning} {readOnly} />
|
<Wordpress bind:service {isRunning} {readOnly} {settings} />
|
||||||
{:else if service.type === 'ghost'}
|
{:else if service.type === 'ghost'}
|
||||||
<Ghost bind:service {readOnly} />
|
<Ghost bind:service {readOnly} />
|
||||||
{:else if service.type === 'meilisearch'}
|
{:else if service.type === 'meilisearch'}
|
||||||
@ -151,17 +167,4 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<!-- <div class="font-bold flex space-x-1 pb-5">
|
|
||||||
<div class="text-xl tracking-tight mr-4">Features</div>
|
|
||||||
</div>
|
|
||||||
<div class="px-4 sm:px-6 pb-10">
|
|
||||||
<ul class="mt-2 divide-y divide-stone-800">
|
|
||||||
<Setting
|
|
||||||
bind:setting={isPublic}
|
|
||||||
on:click={() => changeSettings('isPublic')}
|
|
||||||
title="Set it public"
|
|
||||||
description="Your database will be reachable over the internet. <br>Take security seriously in this case!"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,58 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
import { page } from '$app/stores';
|
||||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { browser } from '$app/env';
|
||||||
|
import { getDomain } from '$lib/components/common';
|
||||||
|
|
||||||
export let service;
|
export let service;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
export let readOnly;
|
export let readOnly;
|
||||||
|
export let settings;
|
||||||
|
const { id } = $page.params;
|
||||||
|
|
||||||
|
let ftpUrl = generateUrl(service.wordpress.ftpPublicPort);
|
||||||
|
let ftpUser = service.wordpress.ftpUser;
|
||||||
|
let ftpPassword = service.wordpress.ftpPassword;
|
||||||
|
let ftpLoading = false;
|
||||||
|
|
||||||
|
function generateUrl(publicPort) {
|
||||||
|
return browser
|
||||||
|
? `sftp://${
|
||||||
|
settings.fqdn ? getDomain(settings.fqdn) : window.location.hostname
|
||||||
|
}:${publicPort}`
|
||||||
|
: 'Loading...';
|
||||||
|
}
|
||||||
|
async function changeSettings(name) {
|
||||||
|
if (ftpLoading) return;
|
||||||
|
if (isRunning) {
|
||||||
|
ftpLoading = true;
|
||||||
|
let ftpEnabled = service.wordpress.ftpEnabled;
|
||||||
|
|
||||||
|
if (name === 'ftpEnabled') {
|
||||||
|
ftpEnabled = !ftpEnabled;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
publicPort,
|
||||||
|
ftpUser: user,
|
||||||
|
ftpPassword: password
|
||||||
|
} = await post(`/services/${id}/wordpress/settings.json`, {
|
||||||
|
ftpEnabled
|
||||||
|
});
|
||||||
|
ftpUrl = generateUrl(publicPort);
|
||||||
|
ftpUser = user;
|
||||||
|
ftpPassword = password;
|
||||||
|
service.wordpress.ftpEnabled = ftpEnabled;
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
ftpLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
@ -28,6 +77,30 @@ define('SUBDOMAIN_INSTALL', false);`
|
|||||||
: 'N/A'}>{service.wordpress.extraConfig}</textarea
|
: 'N/A'}>{service.wordpress.extraConfig}</textarea
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<Setting
|
||||||
|
bind:setting={service.wordpress.ftpEnabled}
|
||||||
|
loading={ftpLoading}
|
||||||
|
disabled={!isRunning}
|
||||||
|
on:click={() => changeSettings('ftpEnabled')}
|
||||||
|
title="Enable sFTP connection to WordPress data"
|
||||||
|
description="Enables an on-demand sFTP connection to the WordPress data directory. This is useful if you want to use sFTP to upload files."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if service.wordpress.ftpEnabled}
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="ftpUrl">sFTP Connection URI</label>
|
||||||
|
<CopyPasswordField id="ftpUrl" readonly disabled name="ftpUrl" value={ftpUrl} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="ftpUser">User</label>
|
||||||
|
<CopyPasswordField id="ftpUser" readonly disabled name="ftpUser" value={ftpUser} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="ftpPassword">Password</label>
|
||||||
|
<CopyPasswordField id="ftpPassword" readonly disabled name="ftpPassword" value={ftpPassword} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">MySQL</div>
|
<div class="title">MySQL</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
const endpoint = `/services/${params.id}.json`;
|
const endpoint = `/services/${params.id}.json`;
|
||||||
const res = await fetch(endpoint);
|
const res = await fetch(endpoint);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const { service, isRunning } = await res.json();
|
const { service, isRunning, settings } = await res.json();
|
||||||
if (!service || Object.entries(service).length === 0) {
|
if (!service || Object.entries(service).length === 0) {
|
||||||
return {
|
return {
|
||||||
status: 302,
|
status: 302,
|
||||||
@ -45,7 +45,8 @@
|
|||||||
stuff: {
|
stuff: {
|
||||||
service,
|
service,
|
||||||
isRunning,
|
isRunning,
|
||||||
readOnly
|
readOnly,
|
||||||
|
settings
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler, supportedServiceTypesAndVersions } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler, supportedServiceTypesAndVersions } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
@ -14,6 +15,7 @@ export const get: RequestHandler = async (event) => {
|
|||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: {
|
||||||
|
type,
|
||||||
versions: supportedServiceTypesAndVersions.find((name) => name.name === type).versions
|
versions: supportedServiceTypesAndVersions.find((name) => name.name === type).versions
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -31,11 +31,16 @@
|
|||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
|
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
|
|
||||||
export let versions;
|
export let versions;
|
||||||
|
export let type;
|
||||||
|
let recommendedVersion = supportedServiceTypesAndVersions.find(
|
||||||
|
({ name }) => name === type
|
||||||
|
)?.recommendedVersion;
|
||||||
async function handleSubmit(version) {
|
async function handleSubmit(version) {
|
||||||
try {
|
try {
|
||||||
await post(`/services/${id}/configuration/version.json`, { version });
|
await post(`/services/${id}/configuration/version.json`, { version });
|
||||||
@ -49,13 +54,26 @@
|
|||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex space-x-1 p-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">Select a Service version</div>
|
<div class="mr-4 text-2xl tracking-tight">Select a Service version</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if from}
|
||||||
|
<div class="pb-10 text-center">
|
||||||
|
Warning: you are about to change the version of this service.<br />This could cause problem
|
||||||
|
after you restart the service,
|
||||||
|
<span class="font-bold text-pink-600">like losing your data, incompatibility issues, etc</span
|
||||||
|
>.<br />Only do if you know what you are doing.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
{#each versions as version}
|
{#each versions as version}
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<form on:submit|preventDefault={() => handleSubmit(version)}>
|
<form on:submit|preventDefault={() => handleSubmit(version)}>
|
||||||
<button type="submit" class="box-selection text-xl font-bold hover:bg-pink-600"
|
<button
|
||||||
>{version}</button
|
type="submit"
|
||||||
|
class:bg-pink-500={recommendedVersion === version}
|
||||||
|
class="box-selection relative flex text-xl font-bold hover:bg-pink-600"
|
||||||
|
>{version}
|
||||||
|
{#if recommendedVersion === version}
|
||||||
|
<span class="absolute bottom-0 pb-2 text-xs">recommended</span>
|
||||||
|
{/if}</button
|
||||||
>
|
>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -75,7 +76,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.ghost.environmentVariables[secret.name] = secret.value;
|
config.ghost.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -17,7 +17,7 @@ export const get: RequestHandler = async (event) => {
|
|||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
try {
|
try {
|
||||||
const service = await db.getService({ id, teamId });
|
const service = await db.getService({ id, teamId });
|
||||||
const { destinationDockerId, destinationDocker, type, version } = service;
|
const { destinationDockerId, destinationDocker, type, version, settings } = service;
|
||||||
|
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
@ -46,7 +46,8 @@ export const get: RequestHandler = async (event) => {
|
|||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
isRunning,
|
isRunning,
|
||||||
service
|
service,
|
||||||
|
settings
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
props: {
|
props: {
|
||||||
service: stuff.service,
|
service: stuff.service,
|
||||||
isRunning: stuff.isRunning,
|
isRunning: stuff.isRunning,
|
||||||
readOnly: stuff.readOnly
|
readOnly: stuff.readOnly,
|
||||||
|
settings: stuff.settings
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -37,6 +38,7 @@
|
|||||||
export let service;
|
export let service;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
export let readOnly;
|
export let readOnly;
|
||||||
|
export let settings;
|
||||||
|
|
||||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
@ -76,4 +78,4 @@
|
|||||||
<ServiceLinks {service} />
|
<ServiceLinks {service} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Services bind:service {isRunning} {readOnly} />
|
<Services bind:service {isRunning} {readOnly} {settings} />
|
||||||
|
@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.updateMeiliSearchService({ id, fqdn, name });
|
await db.updateService({ id, fqdn, name });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -32,7 +33,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.updateLanguageToolService({ id, fqdn, name });
|
await db.updateService({ id, fqdn, name });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -37,7 +38,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -8,6 +8,7 @@ import getPort, { portNumbers } from 'get-port';
|
|||||||
import { getDomain } from '$lib/components/common';
|
import { getDomain } from '$lib/components/common';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -55,7 +56,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -33,7 +34,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -30,7 +31,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -120,7 +121,7 @@ COPY ./init.query /docker-entrypoint-initdb.d/init.query
|
|||||||
COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||||
|
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -31,7 +32,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -12,7 +12,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.updateVaultWardenService({ id, fqdn, name });
|
await db.updateService({ id, fqdn, name });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { getServiceImage, ErrorHandler } from '$lib/database';
|
import { getServiceImage, ErrorHandler } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -32,7 +33,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.updateVsCodeServer({ id, fqdn, name });
|
await db.updateService({ id, fqdn, name });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -41,7 +42,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.environmentVariables[secret.name] = secret.value;
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
187
src/routes/services/[id]/wordpress/settings.json.ts
Normal file
187
src/routes/services/[id]/wordpress/settings.json.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import { dev } from '$app/env';
|
||||||
|
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
|
||||||
|
import { decrypt, encrypt } from '$lib/crypto';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { generateDatabaseConfiguration, ErrorHandler, generatePassword } from '$lib/database';
|
||||||
|
import { checkContainer, startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import cuid from 'cuid';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import getPort, { portNumbers } from 'get-port';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
export const post: RequestHandler = async (event) => {
|
||||||
|
const { status, body, teamId } = await getUserDetails(event);
|
||||||
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
|
const data = await db.prisma.setting.findFirst();
|
||||||
|
const { minPort, maxPort } = data;
|
||||||
|
|
||||||
|
const { ftpEnabled } = await event.request.json();
|
||||||
|
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
|
||||||
|
let ftpUser = cuid();
|
||||||
|
let ftpPassword = generatePassword();
|
||||||
|
|
||||||
|
const hostkeyDir = dev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
||||||
|
try {
|
||||||
|
const data = await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpEnabled },
|
||||||
|
include: { service: { include: { destinationDocker: true } } }
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
service: { destinationDockerId, destinationDocker },
|
||||||
|
ftpPublicPort: oldPublicPort,
|
||||||
|
ftpUser: user,
|
||||||
|
ftpPassword: savedPassword,
|
||||||
|
ftpHostKey,
|
||||||
|
ftpHostKeyPrivate
|
||||||
|
} = data;
|
||||||
|
if (user) ftpUser = user;
|
||||||
|
if (savedPassword) ftpPassword = decrypt(savedPassword);
|
||||||
|
|
||||||
|
const { stdout: password } = await asyncExecShell(
|
||||||
|
`echo ${ftpPassword} | openssl passwd -1 -stdin`
|
||||||
|
);
|
||||||
|
if (destinationDockerId) {
|
||||||
|
try {
|
||||||
|
await fs.stat(hostkeyDir);
|
||||||
|
} catch (error) {
|
||||||
|
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
|
||||||
|
}
|
||||||
|
if (!ftpHostKey) {
|
||||||
|
await asyncExecShell(
|
||||||
|
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
|
||||||
|
);
|
||||||
|
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpHostKey: encrypt(ftpHostKey) }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
|
||||||
|
}
|
||||||
|
if (!ftpHostKeyPrivate) {
|
||||||
|
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
|
||||||
|
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
|
||||||
|
}
|
||||||
|
const { network, engine } = destinationDocker;
|
||||||
|
const host = getEngine(engine);
|
||||||
|
if (ftpEnabled) {
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: {
|
||||||
|
ftpPublicPort: publicPort,
|
||||||
|
ftpUser: user ? undefined : ftpUser,
|
||||||
|
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const isRunning = await checkContainer(engine, `${id}-ftp`);
|
||||||
|
if (isRunning) {
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
//
|
||||||
|
}
|
||||||
|
const volumes = [
|
||||||
|
`${id}-wordpress-data:/home/${ftpUser}`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
||||||
|
];
|
||||||
|
|
||||||
|
const compose: ComposeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[`${id}-ftp`]: {
|
||||||
|
image: `atmoz/sftp:alpine`,
|
||||||
|
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:1001'`,
|
||||||
|
extra_hosts: ['host.docker.internal:host-gateway'],
|
||||||
|
container_name: `${id}-ftp`,
|
||||||
|
volumes,
|
||||||
|
networks: [network],
|
||||||
|
depends_on: [],
|
||||||
|
restart: 'always'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${id}-wordpress-data`]: {
|
||||||
|
external: true,
|
||||||
|
name: `${id}-wordpress-data`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await fs.writeFile(
|
||||||
|
`${hostkeyDir}/${id}.sh`,
|
||||||
|
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key`
|
||||||
|
);
|
||||||
|
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
|
||||||
|
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
|
||||||
|
);
|
||||||
|
|
||||||
|
await startTcpProxy(destinationDocker, `${id}-ftp`, publicPort, 22);
|
||||||
|
} else {
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpPublicPort: null }
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ftpEnabled) {
|
||||||
|
return {
|
||||||
|
status: 201,
|
||||||
|
body: {
|
||||||
|
publicPort,
|
||||||
|
ftpUser,
|
||||||
|
ftpPassword
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return ErrorHandler(error);
|
||||||
|
} finally {
|
||||||
|
await asyncExecShell(
|
||||||
|
`rm -f ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -65,7 +66,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.wordpress.environmentVariables[secret.name] = secret.value;
|
config.wordpress.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
||||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
||||||
import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
|
import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
|
||||||
|
import { session } from '$app/stores';
|
||||||
|
|
||||||
export let services;
|
export let services;
|
||||||
async function newService() {
|
async function newService() {
|
||||||
@ -74,6 +75,9 @@
|
|||||||
<div class="font-bold text-xl text-center truncate">
|
<div class="font-bold text-xl text-center truncate">
|
||||||
{service.name}
|
{service.name}
|
||||||
</div>
|
</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
|
<div class="text-center truncate">Team {service.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
{#if !service.type || !service.fqdn}
|
{#if !service.type || !service.fqdn}
|
||||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||||
Configuration missing
|
Configuration missing
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !source.gitlabApp?.appId}
|
{#if !source.gitlabApp?.appId}
|
||||||
|
<div>
|
||||||
<form class="grid grid-flow-row gap-2 py-4" on:submit|preventDefault={newApp}>
|
<form class="grid grid-flow-row gap-2 py-4" on:submit|preventDefault={newApp}>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="type">GitLab Application Type</label>
|
<label for="type">GitLab Application Type</label>
|
||||||
@ -178,6 +179,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="mx-auto max-w-4xl px-6">
|
<div class="mx-auto max-w-4xl px-6">
|
||||||
<form on:submit|preventDefault={handleSubmitSave} class="py-4">
|
<form on:submit|preventDefault={handleSubmitSave} class="py-4">
|
||||||
|
@ -60,6 +60,9 @@
|
|||||||
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
||||||
>
|
>
|
||||||
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
||||||
|
{#if $session.teamId === '0'}
|
||||||
|
<div class="text-center truncate">Team {source.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
{#if (source.type === 'gitlab' && !source.gitlabAppId) || (source.type === 'github' && !source.githubAppId && !source.githubApp?.installationId)}
|
{#if (source.type === 'gitlab' && !source.gitlabAppId) || (source.type === 'github' && !source.githubAppId && !source.githubApp?.installationId)}
|
||||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||||
Configuration missing
|
Configuration missing
|
||||||
|
@ -4,14 +4,14 @@ import { ErrorHandler } from '$lib/database';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { userId, status, body } = await getUserDetails(event, false);
|
const { teamId, userId, status, body } = await getUserDetails(event, false);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await db.prisma.user.findFirst({
|
const user = await db.prisma.user.findFirst({
|
||||||
where: { id: userId, teams: { some: { id } } },
|
where: { id: userId, teams: teamId === '0' ? undefined : { some: { id } } },
|
||||||
include: { permission: true }
|
include: { permission: true }
|
||||||
});
|
});
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -4,14 +4,15 @@ import { ErrorHandler } from '$lib/database';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { userId, status, body } = await getUserDetails(event, false);
|
const { teamId, userId, status, body } = await getUserDetails(event, false);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const teams = await db.prisma.permission.findMany({
|
const teams = await db.prisma.permission.findMany({
|
||||||
where: { userId },
|
where: { userId: teamId === '0' ? undefined : teamId },
|
||||||
include: { team: { include: { _count: { select: { users: true } } } } }
|
include: { team: { include: { _count: { select: { users: true } } } } }
|
||||||
});
|
});
|
||||||
|
|
||||||
const invitations = await db.prisma.teamInvitation.findMany({ where: { uid: userId } });
|
const invitations = await db.prisma.teamInvitation.findMany({ where: { uid: userId } });
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
Loading…
Reference in New Issue
Block a user