Merged v2.4.0
This commit is contained in:
@@ -134,13 +134,18 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>Coolify</title>
|
||||
{#if !$session.whiteLabeled}
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
|
||||
{#if $session.userId}
|
||||
<nav class="nav-main">
|
||||
<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
|
||||
<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 !$session.whiteLabeled}
|
||||
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
|
||||
{/if}
|
||||
<div class="flex flex-col space-y-4 py-2" class:mt-2={$session.whiteLabeled}>
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
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" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="border-t border-stone-700" />
|
||||
</div>
|
||||
<div class="flex-1" />
|
||||
|
||||
@@ -514,6 +518,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</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
|
||||
class="fixed right-0 bottom-0 z-50 m-2 w-64 bg-opacity-30 p-2 px-4"
|
||||
bind:value={selectedTeamId}
|
||||
|
||||
@@ -81,6 +81,9 @@
|
||||
);
|
||||
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 composerPHP = files.find(
|
||||
(file) => file.name === 'composer.json' && file.type === 'blob'
|
||||
);
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
@@ -103,7 +106,7 @@
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP) {
|
||||
} else if (indexPHP || composerPHP) {
|
||||
foundConfig = findBuildPack('php');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
@@ -127,6 +130,9 @@
|
||||
);
|
||||
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 composerPHP = files.find(
|
||||
(file) => file.name === 'composer.json' && file.type === 'file'
|
||||
);
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
@@ -146,7 +152,7 @@
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP) {
|
||||
} else if (indexPHP || composerPHP) {
|
||||
foundConfig = findBuildPack('php');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<div class="flex flex-col justify-center">
|
||||
{#if !filteredSources || filteredSources.length === 0}
|
||||
<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">
|
||||
<a href="/new/source" sveltekit:prefetch class="add-icon bg-orange-600 hover:bg-orange-500">
|
||||
<svg
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
||||
import Astro from '$lib/components/svg/applications/Astro.svelte';
|
||||
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
const buildPack = application?.buildPack?.toLowerCase();
|
||||
</script>
|
||||
@@ -54,6 +55,9 @@
|
||||
{/if}
|
||||
|
||||
<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}
|
||||
<div class="truncate text-center">{application.fqdn}</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { supportedDatabaseTypesAndVersions } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { supportedDatabaseTypesAndVersions } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { makeLabelForStandaloneDatabase } from '$lib/buildPacks/common';
|
||||
import { startTcpProxy } from '$lib/haproxy';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (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 composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
||||
import { post } from '$lib/api';
|
||||
import { goto } from '$app/navigation';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
async function newDatabase() {
|
||||
const { id } = await post('/databases/new', {});
|
||||
@@ -59,6 +60,9 @@
|
||||
<div class="font-bold text-xl text-center truncate">
|
||||
{database.name}
|
||||
</div>
|
||||
{#if $session.teamId === '0'}
|
||||
<div class="text-center truncate">Team {database.teams[0].name}</div>
|
||||
{/if}
|
||||
{#if !database.type}
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
|
||||
@@ -184,41 +184,19 @@
|
||||
value={destination.network}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
disabled={cannotDisable}
|
||||
bind:setting={destination.isCoolifyProxyUsed}
|
||||
on:click={changeProxySetting}
|
||||
title="Use Coolify Proxy?"
|
||||
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||
cannotDisable
|
||||
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</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 $session.teamId === '0'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
disabled={cannotDisable}
|
||||
bind:setting={destination.isCoolifyProxyUsed}
|
||||
on:click={changeProxySetting}
|
||||
title="Use Coolify Proxy?"
|
||||
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||
cannotDisable
|
||||
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{/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} -->
|
||||
</form>
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
console.log(teamId);
|
||||
const { id } = event.params;
|
||||
try {
|
||||
const destination = await db.getDestination({ id, teamId });
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
<a href="/destinations/{destination.id}" class="no-underline p-2 w-96">
|
||||
<div class="box-selection hover:bg-sky-600">
|
||||
<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>
|
||||
</a>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
export let payload;
|
||||
|
||||
@@ -56,13 +57,15 @@
|
||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={payload.isCoolifyProxyUsed}
|
||||
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||
title="Use Coolify 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>
|
||||
{#if $session.teamId === '0'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={payload.isCoolifyProxyUsed}
|
||||
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||
title="Use Coolify 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>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
export let service;
|
||||
export let isRunning;
|
||||
export let readOnly;
|
||||
export let settings;
|
||||
|
||||
import { page, session } from '$app/stores';
|
||||
import { post } from '$lib/api';
|
||||
@@ -91,7 +92,22 @@
|
||||
/>
|
||||
</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">
|
||||
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
||||
<div>
|
||||
@@ -143,7 +159,7 @@
|
||||
{:else if service.type === 'vscodeserver'}
|
||||
<VsCodeServer {service} />
|
||||
{:else if service.type === 'wordpress'}
|
||||
<Wordpress bind:service {isRunning} {readOnly} />
|
||||
<Wordpress bind:service {isRunning} {readOnly} {settings} />
|
||||
{:else if service.type === 'ghost'}
|
||||
<Ghost bind:service {readOnly} />
|
||||
{:else if service.type === 'meilisearch'}
|
||||
@@ -151,17 +167,4 @@
|
||||
{/if}
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@@ -1,9 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { post } from '$lib/api';
|
||||
import { page } from '$app/stores';
|
||||
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 isRunning;
|
||||
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>
|
||||
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
@@ -28,6 +77,30 @@ define('SUBDOMAIN_INSTALL', false);`
|
||||
: 'N/A'}>{service.wordpress.extraConfig}</textarea
|
||||
>
|
||||
</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="title">MySQL</div>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
const endpoint = `/services/${params.id}.json`;
|
||||
const res = await fetch(endpoint);
|
||||
if (res.ok) {
|
||||
const { service, isRunning } = await res.json();
|
||||
const { service, isRunning, settings } = await res.json();
|
||||
if (!service || Object.entries(service).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -45,7 +45,8 @@
|
||||
stuff: {
|
||||
service,
|
||||
isRunning,
|
||||
readOnly
|
||||
readOnly,
|
||||
settings
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler, supportedServiceTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler, supportedServiceTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -14,6 +15,7 @@ export const get: RequestHandler = async (event) => {
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
type,
|
||||
versions: supportedServiceTypesAndVersions.find((name) => name.name === type).versions
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,11 +31,16 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { goto } from '$app/navigation';
|
||||
import { post } from '$lib/api';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
|
||||
export let versions;
|
||||
export let type;
|
||||
let recommendedVersion = supportedServiceTypesAndVersions.find(
|
||||
({ name }) => name === type
|
||||
)?.recommendedVersion;
|
||||
async function handleSubmit(version) {
|
||||
try {
|
||||
await post(`/services/${id}/configuration/version.json`, { version });
|
||||
@@ -49,13 +54,26 @@
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Select a Service version</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">
|
||||
{#each versions as version}
|
||||
<div class="p-2">
|
||||
<form on:submit|preventDefault={() => handleSubmit(version)}>
|
||||
<button type="submit" class="box-selection text-xl font-bold hover:bg-pink-600"
|
||||
>{version}</button
|
||||
<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>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (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;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const get: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, type, version } = service;
|
||||
const { destinationDockerId, destinationDocker, type, version, settings } = service;
|
||||
|
||||
let isRunning = false;
|
||||
if (destinationDockerId) {
|
||||
@@ -46,7 +46,8 @@ export const get: RequestHandler = async (event) => {
|
||||
return {
|
||||
body: {
|
||||
isRunning,
|
||||
service
|
||||
service,
|
||||
settings
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
props: {
|
||||
service: stuff.service,
|
||||
isRunning: stuff.isRunning,
|
||||
readOnly: stuff.readOnly
|
||||
readOnly: stuff.readOnly,
|
||||
settings: stuff.settings
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -37,6 +38,7 @@
|
||||
export let service;
|
||||
export let isRunning;
|
||||
export let readOnly;
|
||||
export let settings;
|
||||
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
@@ -76,4 +78,4 @@
|
||||
<ServiceLinks {service} />
|
||||
</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();
|
||||
|
||||
try {
|
||||
await db.updateMeiliSearchService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -32,7 +33,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
await db.updateLanguageToolService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -37,7 +38,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -8,6 +8,7 @@ import getPort, { portNumbers } from 'get-port';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -55,7 +56,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -33,7 +34,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -30,7 +31,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (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`;
|
||||
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -31,7 +32,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const post: RequestHandler = async (event) => {
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
await db.updateVaultWardenService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { getServiceImage, ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -32,7 +33,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
await db.updateVsCodeServer({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -41,7 +42,7 @@ export const post: RequestHandler = async (event) => {
|
||||
config.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[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 { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
|
||||
export const post: RequestHandler = async (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;
|
||||
});
|
||||
}
|
||||
const composeFile = {
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
||||
import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
export let services;
|
||||
async function newService() {
|
||||
@@ -74,6 +75,9 @@
|
||||
<div class="font-bold text-xl text-center truncate">
|
||||
{service.name}
|
||||
</div>
|
||||
{#if $session.teamId === '0'}
|
||||
<div class="text-center truncate">Team {service.teams[0].name}</div>
|
||||
{/if}
|
||||
{#if !service.type || !service.fqdn}
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
|
||||
@@ -91,93 +91,95 @@
|
||||
</script>
|
||||
|
||||
{#if !source.gitlabApp?.appId}
|
||||
<form class="grid grid-flow-row gap-2 py-4" on:submit|preventDefault={newApp}>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="type">GitLab Application Type</label>
|
||||
<select name="type" id="type" class="w-96" bind:value={payload.applicationType}>
|
||||
<option value="user">User owned application</option>
|
||||
<option value="group">Group owned application</option>
|
||||
{#if source.htmlUrl !== 'https://gitlab.com'}
|
||||
<option value="instance">Instance-wide application (self-hosted)</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
{#if payload.applicationType === 'group'}
|
||||
<div>
|
||||
<form class="grid grid-flow-row gap-2 py-4" on:submit|preventDefault={newApp}>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="groupName">Group Name</label>
|
||||
<input name="groupName" id="groupName" required bind:value={payload.groupName} />
|
||||
<label for="type">GitLab Application Type</label>
|
||||
<select name="type" id="type" class="w-96" bind:value={payload.applicationType}>
|
||||
<option value="user">User owned application</option>
|
||||
<option value="group">Group owned application</option>
|
||||
{#if source.htmlUrl !== 'https://gitlab.com'}
|
||||
<option value="instance">Instance-wide application (self-hosted)</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
{#if payload.applicationType === 'group'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="groupName">Group Name</label>
|
||||
<input name="groupName" id="groupName" required bind:value={payload.groupName} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="w-full pt-10 text-center">
|
||||
<button class="w-96 bg-orange-600 hover:bg-orange-500" type="submit"
|
||||
>Register new OAuth application on GitLab</button
|
||||
>
|
||||
</div>
|
||||
<div class="w-full pt-10 text-center">
|
||||
<button class="w-96 bg-orange-600 hover:bg-orange-500" type="submit"
|
||||
>Register new OAuth application on GitLab</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<Explainer
|
||||
customClass="w-full"
|
||||
text="<span class='font-bold text-base text-white'>Scopes required:</span>
|
||||
<Explainer
|
||||
customClass="w-full"
|
||||
text="<span class='font-bold text-base text-white'>Scopes required:</span>
|
||||
<br>- <span class='text-orange-500 font-bold'>api</span> (Access the authenticated user's API)
|
||||
<br>- <span class='text-orange-500 font-bold'>read_repository</span> (Allows read-only access to the repository)
|
||||
<br>- <span class='text-orange-500 font-bold'>email</span> (Allows read-only access to the user's primary email address using OpenID Connect)
|
||||
<br>
|
||||
<br>For extra security, you can set Expire access tokens!
|
||||
<br><br>Webhook URL: <span class='text-orange-500 font-bold'>{browser
|
||||
? window.location.origin
|
||||
: ''}/webhooks/gitlab</span>
|
||||
? window.location.origin
|
||||
: ''}/webhooks/gitlab</span>
|
||||
<br>But if you will set a custom domain name for Coolify, use that instead."
|
||||
/>
|
||||
</form>
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4 pt-10">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-orange-600={!loading}
|
||||
class:hover:bg-orange-500={!loading}
|
||||
disabled={loading}>{loading ? 'Saving...' : 'Save'}</button
|
||||
>
|
||||
</div>
|
||||
/>
|
||||
</form>
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4 pt-10">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-orange-600={!loading}
|
||||
class:hover:bg-orange-500={!loading}
|
||||
disabled={loading}>{loading ? 'Saving...' : 'Save'}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-start">
|
||||
<div class="flex-col">
|
||||
<label for="oauthId" class="pt-2">OAuth ID</label>
|
||||
<Explainer
|
||||
text="The OAuth ID is the unique identifier of the GitLab application. <br>You can find it <span class='font-bold text-orange-600' >in the URL</span> of your GitLab OAuth Application."
|
||||
<div class="grid grid-cols-2 items-start">
|
||||
<div class="flex-col">
|
||||
<label for="oauthId" class="pt-2">OAuth ID</label>
|
||||
<Explainer
|
||||
text="The OAuth ID is the unique identifier of the GitLab application. <br>You can find it <span class='font-bold text-orange-600' >in the URL</span> of your GitLab OAuth Application."
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
on:change={checkOauthId}
|
||||
bind:this={oauthIdEl}
|
||||
name="oauthId"
|
||||
id="oauthId"
|
||||
type="number"
|
||||
required
|
||||
bind:value={payload.oauthId}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
on:change={checkOauthId}
|
||||
bind:this={oauthIdEl}
|
||||
name="oauthId"
|
||||
id="oauthId"
|
||||
type="number"
|
||||
required
|
||||
bind:value={payload.oauthId}
|
||||
/>
|
||||
</div>
|
||||
{#if payload.applicationType === 'group'}
|
||||
{#if payload.applicationType === 'group'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="groupName">Group Name</label>
|
||||
<input name="groupName" id="groupName" required bind:value={payload.groupName} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="groupName">Group Name</label>
|
||||
<input name="groupName" id="groupName" required bind:value={payload.groupName} />
|
||||
<label for="appId">Application ID</label>
|
||||
<input name="appId" id="appId" required bind:value={payload.appId} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="appId">Application ID</label>
|
||||
<input name="appId" id="appId" required bind:value={payload.appId} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="appSecret">Secret</label>
|
||||
<input
|
||||
name="appSecret"
|
||||
id="appSecret"
|
||||
type="password"
|
||||
required
|
||||
bind:value={payload.appSecret}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="appSecret">Secret</label>
|
||||
<input
|
||||
name="appSecret"
|
||||
id="appSecret"
|
||||
type="password"
|
||||
required
|
||||
bind:value={payload.appSecret}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<form on:submit|preventDefault={handleSubmitSave} class="py-4">
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
||||
>
|
||||
<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)}
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
|
||||
@@ -4,14 +4,14 @@ import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
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 };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
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 }
|
||||
});
|
||||
if (!user) {
|
||||
|
||||
@@ -4,14 +4,15 @@ import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
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 };
|
||||
|
||||
try {
|
||||
const teams = await db.prisma.permission.findMany({
|
||||
where: { userId },
|
||||
where: { userId: teamId === '0' ? undefined : teamId },
|
||||
include: { team: { include: { _count: { select: { users: true } } } } }
|
||||
});
|
||||
|
||||
const invitations = await db.prisma.teamInvitation.findMany({ where: { uid: userId } });
|
||||
return {
|
||||
status: 200,
|
||||
|
||||
Reference in New Issue
Block a user